美文网首页算法专题ios 开发
Swift 排序算法代码实现

Swift 排序算法代码实现

作者: iOS小洁 | 来源:发表于2022-11-24 21:25 被阅读0次

冒泡排序

逐个比较并移动,每次循环确定一个最大元素

复杂度:最好O(n),最坏,平均复杂度O(n^2), 空间复杂度O(1)。

稳定性:冒泡排序稳定与否,取决于判断相邻两个元素大小的的方式是> 或者是 >=

static func bublleSort(arr: [Int]) -> [Int] {
    var list = arr;
    for end in (1..<list.count - 1).reversed() {
        for j in 0...end {
            if list[j] > list[j+1] {
                (list[j], list[j+1]) = (list[j+1], list[j])
            }
        }
    }
    return list
}

优化1:序列如果完全有序,可以提前终止冒泡

优化2:尾部有序,可以通过记录最后一次交换的位置,减少比较次数

原地算法:不依赖额外的资源或者依赖少数的额外资源,仅依输出来覆盖输入

选择排序

从待排序数组中找到最大元素的位置,并与最后面的元素交换位置。

复杂度:最好,最坏,平均复杂度O(n^2), 空间复杂度O(1)。

稳定性:不稳定

static func sort(arr: [Int]) -> [Int] {
    if arr.count <= 1 {return arr}
    var list = arr;
    for end in (0..<list.count-1).reversed(){
        var maxIndex = 0;
        for start in 0..<end {
            if list[start] > list[maxIndex] {
                maxIndex = start;
            }
        }
        (list[maxIndex], list[end]) = (list[end], list[maxIndex])
    }
    return list
}

选择排序交换次数远小于冒泡排序,平均性能优于冒泡

不稳定排序,如数组[2 3 2 1 4],2和1交换后,两个2的顺序便不稳定

优化:使用堆来选择最大值

堆排序

将无序的序列构建成大顶堆。

复杂度:最好,最坏,平均复杂度O(nlogn), 空间复杂度O(1)。

稳定性:不稳定

完全二叉树:假设根节点的编号是 i ,那么该根节点的左孩子的编号是 2i,右孩子的编号是 2i+1。

大顶堆:完全二叉树的根节点比其左右节点都要大

具体步骤

  1. 对序列进行原地建堆
  2. 重复
    1. 交换堆顶元素与堆尾元素
    2. 堆元素数量减1
    3. 对0位置进行一次siftDown操作
class HeapSort: ArraySort {
    
    var list : [Int]?
    static var heapSize = 0
    
    static func sort(arr: [Int]) -> [Int] {
        if arr.count <= 1 {return arr}
        var list = arr;
        heapSize = arr.count
          //1、构建大顶堆
        for i in (0..<heapSize>>1).reversed() {
            siftDown(arr: &list, index: i);
        }
        //2、首尾元素交换,并重新处理首元素的位置。此时二叉堆元素数量-1
        while(heapSize > 1) {
            heapSize -= 1;
            list.swapAt(0, heapSize) // 交换二叉堆首尾元素,目的是不需要额外空间
            siftDown(arr: &list, index: 0);
        }
        return list
    }
    
    //调整大顶堆(仅是调整过程,建立在大顶堆以构建的基础上)
    static func siftDown(arr : inout [Int], index : Int){
        var tempIndex = index;
        
        let temp = arr[tempIndex];
        let half = heapSize >> 1
      
        while tempIndex < half {
            var childIndex = (tempIndex << 1)+1
            var child = arr[childIndex]
            
            let rightIndex = childIndex + 1
            
            if rightIndex < heapSize && arr[rightIndex] > child {
                
                child = arr[rightIndex];
                childIndex = rightIndex;
            }
            
            if temp > child { break }
            
            arr[tempIndex] = child;
            tempIndex = childIndex
        }
        arr[tempIndex] = temp
    }
}

插入排序

将数据分为两部分,即已排序部分,和未排序部分。逐个将未排序的的元素,插入到已排序的元素中。

逆序对:数组中从前往后的,大小与目的顺序相反的一对元素组成一个逆序对

复杂度:最好O(n),最坏,平均复杂度O(n^2), 空间复杂度O(1)。逆序对越多,时间复杂度越高

稳定性:稳定

static func sort(arr: [Int]) -> [Int] {
  if arr.count < 2 {return arr}
  var list = arr
  for i in 1..<list.count {
    var index = i
    while index > 0 && list[index] < list[index - 1]{
      list.swapAt(index, index - 1)
      index -= 1
    }
  }
  return list
}

优化1:将交换转为挪动

  1. 备份待插入元素temp
  2. 头部有序数据中比temp大的,往右边挪动一个位置
  3. 将temp插入到最终合适的位置
static func sort(arr: [Int]) -> [Int] {
  if arr.count < 2 {return arr}
  var list = arr
  for i in 1..<list.count {
    var index = i
    let temp = list[index];
    while index > 0 && temp < list[index - 1]{
      list[index] = list[index - 1]
      index -= 1
    }
    list[index] = temp
  }
  return list
}

优化2:二分查找

class InsertionSort2: ArraySort {
    
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 {return arr}
        var list = arr
        for i in 1..<list.count {
            let dest = InsertionSort2.search(arr: list, index: i)
            InsertionSort2.insert(arr: &list, source: i, dest: dest)
        }
        return list
    }
    
    static func insert(arr: inout [Int], source: Int, dest: Int) {
        
        if dest == source { return }
        let temp = arr[source];
        for i in ((dest+1)...source).reversed() {
            arr[i] = arr[i - 1];
        }
        arr[dest] = temp
    }
 
    static func search(arr: [Int], index: Int) -> Int {
        var begin = 0
        var end = index
        
        while(begin < end) {
            let mid = (begin + end) >> 1
            if (arr[mid] > arr[index]) {
                end = mid
            } else {
                begin = mid + 1
            }
        }
        return begin
    }
}

归并排序

递归将当前序列分割成两个子序列,直到不能被分割位置。然后将子序列合并成一个有序的序列

复杂度:最好,最坏,平均复杂度O(nlogn), 空间复杂度O(n/2 + logn)=O(n)。n/2是临时存放左侧数组,logn是因为递归调用

稳定性:稳定

class MergeSort: ArraySort {
    
    static var tempList: [Int] = []
    
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 {return arr}
        var list = arr
        
        MergeSort.divide(arr: &list, begin: 0, end: list.count)
        
        return list
    }
    
    static func divide(arr: inout [Int], begin: Int, end: Int) {
        
        if end - begin < 2 { return }
        let mid = (begin + end) >> 1
        divide(arr: &arr, begin: begin, end: mid)
        divide(arr: &arr, begin: mid, end: end)
        
        MergeSort.merge(arr: &arr, begin: begin, mid: mid, end: end)
    }
    
    static func merge(arr: inout [Int], begin: Int, mid: Int, end: Int) {
        var li = 0
        let le = mid - begin
        var ri = mid
        let re = end
        var ai = begin
        
        MergeSort.tempList = []
        for i in li..<le {
            MergeSort.tempList.append(arr[begin + i]) 
        }
        
        while(li < le) {
            if ri < re && arr[ri] < arr[li] {
                arr[ai] = arr[ri]
                ai += 1
                ri += 1
            }else {
                arr[ai] = MergeSort.tempList[li]
                ai += 1
                li += 1
            }
        }
    }
}

快速排序

选择轴点元素pivot,利用pivot将序列分两个子序列,对子序列递归以上操作

复杂度:最好,平均复杂度O(nlogn), 最坏O(n^2),空间复杂度O(logn)。logn是因为递归调用

稳定性:不稳定

判断轴点元素与序列中元素大小时,用 < 或 > ,不包含=的意义:避免大小相等的元素过多,导致子序列分配不均匀,出现最坏时间复杂度

与归并排序对比,表面看时间复杂度比归并排序更高,但实际上是比归并排序快的。归并排序是每个元素都要一直参与合并(2-4-6-8)。快排每次递归都能够确定一个元素的位置,相当于数据规模一直在减少

可以通过随机数的方式减少最坏情况出现的几率。可以每次在区间中随机选择一个元素左边元素交换位置

class QuickSort: ArraySort {
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 { return arr }
        var list = arr
        quckSort(arr: &list, left: 0, right: arr.count)
        return list
    }
    
    static func quckSort(arr: inout [Int], left: Int, right: Int) {
        
        if right - left < 2 { return } // right - left = 要排序区间的长度
        
        let pivot = pivotIndex(arr: &arr, left: left, right: right)
        
        quckSort(arr: &arr, left: left, right: pivot)
        quckSort(arr: &arr, left: pivot + 1, right: right)
        
    }
    
    static func pivotIndex(arr: inout [Int], left: Int, right: Int) -> Int {
        
          let random = Int.random(in: 0..<(right-left))
        (arr[left], arr[left + random]) = (arr[left + random], arr[left]) // 随机选择一个元素作为轴点元素。避免最坏时间复杂度情况出现
      
        var begin = left
        var end = right
        
        let temp = arr[begin];
        end -= 1 // 最后一个元素index需要-1
        
        while begin < end {
            
            while begin < end {
                if temp < arr[end] {
                    end -= 1
                }else {
                    arr[begin] = arr[end];
                    begin += 1
                    break
                }
            }
            
            while begin < end {
                if temp > arr[begin] {
                    begin += 1
                }else {
                    arr[end] = arr[begin];
                    end -= 1
                    break
                }
            }
        }
        arr[begin] = temp
        return begin
    }
}

希尔排序

希尔排序将序列看做矩阵,分成n列,将每列进行排序。n逐渐减少,当n为1时,整个序列有序

步长序列:由大到小的n的值的组合。希尔给出的步长序列为 n/2^k。不同的步长序列,执行效率不同

在步长逐渐减少的过程中,逆序对数量也在逐渐减少,可以理解为插入排序的改进版

class ShellSort: ArraySort {
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 { return arr }
        var list = arr
        // 步长随便写的
        let stepList = sedgewichStepSequence(arr: &list);
        
        ArrayPrint.printArr(arr: stepList)
        
        for i in stepList {
            shellSort(arr: &list, step: i);
        }
        return list;
    }
    
    static func shellSort(arr: inout [Int], step: Int) {
        // col 列,对每列进行排序
        for col in 0..<step {
            // 每个元素坐标为,col(列index) + step(步长) * 行数 index
            // col + step 即为该列下一行对应的元素
            for begin in stride(from: col + step, to: arr.count, by: step) {
                var cur = begin;
                // 当前cur位置元素,与上一行的元素大小对比
                // 小于前面的就移动,大于前面的结束
                // 逐个往前插入,类似于插入排序
                while cur > col && arr[cur] < arr[cur - step] {
                    (arr[cur], arr[cur - step]) = (arr[cur - step], arr[cur])
                    cur -= step;
                }
                
            }
        }
    }

    // 目前最好的的步长序列,有robert sedgewick提出
    // 最坏时间复杂度O(n^(4/3))
    static func sedgewichStepSequence(arr: inout [Int]) -> [Int] {
        var stepList: [Int] = []
        var k = 0, step = 0
        while(true) {
            if(k % 2 == 0) {
                let pow = Int(truncating: pow(2, k>>1) as NSNumber)
                step = 1+9*(pow*pow - pow)
            }else {
                let pow1 = Int(truncating: pow(2,(k-1)>>1) as NSNumber)
                let pow2 = Int(truncating: pow(2,(k+1)>>1) as NSNumber)
                step = 1+8*pow1*pow2 - 6*pow2
            }
            if step >= arr.count {break}
            stepList.insert(step, at: 0)
            k+=1;
        }
        return stepList
    }
}

希尔给出的步长序列计算

static func shellStepSequence(arr: [Int]) -> [Int]{
        var stepList: [Int] = []
        var step = arr.count
        var temp = step >> 1
        while temp > 0 {
            step = temp;
            temp = step >> 1
            stepList.append(step);
        }
        return stepList
    }

计数排序

复杂度:最好, 最坏, 平均复杂度O(n+k),空间复杂度O(n+k)。k为整数取值范围

稳定性:取决于代码中counts[i] += counts[i-1]优化部分。看具体需求,稳定的话需要额外O(n+k)时间复杂度,额外的O(n)空间

如果对对象进行排序,对象需要有可以用于排序的整数类型。如班级学生根据年龄排序

class CountSort: ArraySort {
    
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 { return arr }
        var list = arr
        
        countSort(arr: &list)
        
        return list;
    }
    
    static func countSort(arr: inout [Int]) {
        
        // 找出最大值,最小值。
        /**
         用来存储的数组区间可以选择 min -> max区间
         作用:
         - 可以对负数进行排序
         - 可以尽可能地降低空间复杂度
         */
        var max = arr[0];
        var min = arr[0];
        for value in arr {
            if value > max {
                max = value
            }
            if value < min {
                min = value
            }
        }
        
        // 将所有元素加入counts计数表中
        var counts:[Int] = [Int].init(repeating: 0, count: max - min + 1)
        for i in 0..<arr.count {
            counts[arr[i] - min] += 1;
        }
        
        // 每个元素位置存储数据 = 该元素出现次数+比该元素小的所有元素出现次数
        /**
         目的:将不稳定的排序变为稳定排序
         原理:统计完数据后,倒序遍历原数组,可以确定每个元素的具体位置。
         **/
        for i in 1..<counts.count {
            counts[i] += counts[i-1]
        }
        
        // 临时数组,保存结果
        // 每个位置,每取出一个元素,该位置计数-1。
        // 有了前部分优化后,位置计数-1即为当前元素所在位置
        var output:[Int] = [Int].init(repeating: 0, count: arr.count)
        for i in (0..<arr.count).reversed() {
            counts[arr[i] - min] -= 1;
            output[counts[arr[i] - min]] = arr[i]
        }
        
        // 结果数据依次存回原数组
        for i in 0..<arr.count {
            arr[i] = output[i]
        }
    }
}

基数排序

依次对个位数,十位数...进行排序。一定要从低位到高位才能保证最后是有序的

复杂度:最好, 最坏, 平均复杂度O(n+k),空间复杂度O(n+k)。k为整数取值范围

稳定性:稳定

class RadixSort: ArraySort {
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 { return arr }
        var list = arr
        
        radixSort(arr: &list)
        
        return list;
    }
    
    static func radixSort(arr: inout [Int]) {
        var max = arr[0];
        for i in 0..<arr.count {
            if arr[i] > max {
                max = arr[i]
            }
        }
        
        var outPuts = [Int].init(repeating: 0, count: arr.count)
        var counts = [Int].init(repeating: 0, count: 10);
        
        var divider = 1
        while divider <= max {
            
            countingSort(arr: &arr, divider: divider, output: &outPuts, count: &counts)
            divider *= 10
        }
    }
    
    static func countingSort(arr: inout [Int], divider: Int, output: inout [Int], count: inout [Int]) {
        for i in 0..<count.count {
            count[i] = 0
        }
        for i in 0..<arr.count {
            count[arr[i]/divider%10] += 1
        }
        for i in 1..<count.count {
            count[i] += count[i-1]
        }
        for i in (0..<arr.count).reversed() {
            count[arr[i]/divider%10] -= 1
            output[count[arr[i]/divider%10]] = arr[i]
        }
        for i in 0..<arr.count {
            arr[i] = output[i]
        }
    }
}

基数排序方法2

依次根据个位数大小分别加入十个桶,然后再从0-10桶中依次取出各元素。再根据十位数大小重复前面操作

复杂度:最好, 最坏, 平均复杂度O(dn),空间复杂度O(kn+k)。d是最大值位数,k是进制

稳定性:稳定

class RadixSort1: ArraySort {
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 { return arr }
        var list = arr
        
        radixSort(arr: &list)
        
        return list;
    }
    
    static func radixSort(arr: inout [Int]) {
        var max = arr[0];
        for i in 0..<arr.count {
            if arr[i] > max {
                max = arr[i]
            }
        }
        
        // 创建十个桶, 每个桶内可以存储最多arr.count个数据
        var buckets = [[Int]].init(repeating: [Int].init(repeating: 0, count: arr.count), count: 10)
        // 每个桶的元素数量
        var bucketSizes = [Int].init(repeating: 0, count: buckets.count)
        
        var divider = 1
        while divider <= max {
            
            for i in 0..<arr.count {
                let no = arr[i] / divider % 10
                buckets[no][bucketSizes[no]] = arr[i]
                bucketSizes[no] += 1
            }
            
            var index = 0
            for i in 0..<buckets.count {
                for j in 0..<bucketSizes[i] {
                    arr[index] = buckets[i][j]
                    index += 1
                }
                bucketSizes[i] = 0
            }
    
            divider *= 10
        }
    }

}

桶排序

执行步骤:

  1. 创建一定数量的桶
  2. 按照一定的规则,将序列中的元素均匀分配到对应的桶。
  3. 对每个桶进行排序
  4. 将所有的非空桶的元素合并成有序数组

分桶规则:需要根据数据特点来划分,下面代码划分方式为(arr[i] * arr.count)/100

复杂度:空间复杂度O(n+m),m为桶数量。时间复杂度O(n+k),k为nlogn-nlogm

稳定性:稳定

// 桶排序需要对数据根据特征划分桶 测试数据:[33, 51, 27, 81, 43, 39, 38, 72]
class BucketSort: ArraySort {
    static func sort(arr: [Int]) -> [Int] {
        if arr.count < 2 { return arr }
        var list = arr
        
        bucketSort(arr: &list)
        
        return list;
    }
    
    static func bucketSort(arr: inout [Int]) {
        
        var buckets = [[Int]].init(repeating: [], count: arr.count)
        
        for i in 0..<arr.count {
            
            let bucketIndex = (arr[i] * arr.count)/100
            var bucket = buckets[bucketIndex]
            bucket.append(arr[i])
            buckets[bucketIndex] = bucket
        }
        
        var index = 0
        for i in 0..<buckets.count {
            if buckets.count == 0 { continue }
            let blist = QuickSort.sort(arr: buckets[i])
            for n in blist {
                arr[index] = n
                index += 1
            }
        }
    }
}

相关文章

  • Swift 排序算法代码实现

    冒泡排序 逐个比较并移动,每次循环确定一个最大元素 复杂度:最好O(n),最坏,平均复杂度O(n^2), 空间复杂...

  • 算法-选择排序

    算 法:选择排序算法时间复杂度: 选择排序算法概述 选择排序伪代码 选择排序实现 选择排序算法概述 排序算法有许...

  • 排序算法

    排序算法详细介绍点击这里 部分排序代码实现

  • 算法-冒泡排序

    算 法:冒泡排序算法时间复杂度: 冒泡排序算法概述 冒泡排序伪代码 冒泡排序实现 冒泡排序算法概述 冒泡排...

  • 数据结构与算法第七讲 - 排序(上)

    对于排序算法,主要掌握内容如下: 排序算法的实现原理 手写出实现代码 评价及分析算法 本讲内容 如何分析一个排序算...

  • 排序算法最强总结及其代码实现(Python/Java)

    前言 本文总结了常用的全部排序算法,内容包括: 排序算法的定义和思路 排序算法的代码实现:Python和Java,...

  • 算法-插入排序

    算 法:插入排序算法时间复杂度: 插入排序算法描述 插入排序伪代码 插入排序实现 插入排序算法概述 插入排...

  • iOS基本算法之二分法排序

    话不多说,上代码 Objective-C 实现 对以下数组的排序结果如下: Swift实现(swift2.0)

  • 常用排序算法

    常用的排序算法 在此总结一下常用排序算法的代码实现 #include using namespace std;t...

  • js常用的排序算法

    1.冒泡排序: 最简单的排序算法,代码实现:function bubbleSort(arr) {var len ...

网友评论

    本文标题:Swift 排序算法代码实现

    本文链接:https://www.haomeiwen.com/subject/huqtfdtx.html