张不大的博客

排序算法

This is a page about »排序算法«.

十大排序算法

总结

排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 稳定性
冒泡 O(n^2) O(n) O(n^2) O(1) 稳定
选择 O(n^2) O(n^2) O(n^2) O(1) 不稳定
插入 O(n^2) O(n) O(n^2) O(1) 稳定
希尔 O(nlog(n)) O(nlog^2(n)) O(nlog^2(n)) O(1) 不稳定
归并 O(nlog(n)) O(nlog(n)) O(nlog(n)) O(n) 稳定
快速 O(nlog(n)) O(nlog(n)) O(n^2) O(log(n)) 不稳定
堆排序 O(nlog(n)) O(nlog(n)) O(nlog(n)) O(1) 不稳定
计数 O(n + k) O(n + k) O(n + k) O(k) 稳定
O(n + k) O(n + k) O(n^2) O(n + k) 稳定
基数 O(n * k) O(n * k) O(n * k) O(n + k) 稳定

这十大排序其实,硬记是不大好的,应该巧记,以及熟悉它的思维,我想这才是重点,因为很多高级语言都已经可以直接调用封装的排序库

  1. 首先,冒泡排序是,最容易死记硬背的。两轮循环,稳定。

  2. 接着选择排序,[ 左 (i)|(i+1) 右 ],在进行便利的时候,左边是已经有序, 在遍历 i 将后面的未排列的元素中与当前的元素vec[i] 比较,完成当轮循环,确定 […i] 是有序的
    注意:选择排序是不稳定的,(相同元素的相对位置在排序的过程中可能发生变化),举例说明,[5a, 8, 5b, 2, 9] 为了标识,两个元素 5,左边的标记为5a,右边的标记为5b在第一次排序的时候,会变成 [2, 8, 5b, 5a, 9] (循环i = 0) 两个5 的相对位置发生了改变由原来的 [5a … 5b] 改编成 [5b … 5a] (后面选择排序的依次是 [2, 5b, 8, 5a, 9] (循环i = 1) -> [2, 5b, 5a, 8, 9] (循环i = 2) -> [2, 5b, 5a, 8, 9] (循环i = 3))

  3. 接着是插入排序,有 n - 1 次主循环(0~n-2) 然后计算后面的元素 i+1 ,把它插入前面[0 ~ i] 合适的位置 注意是稳定的

  4. 再接着是希尔排序,采用 gap 划分 [ gap -> gap/=2 -> gap >0] 然后用 插入排序的 伪代码逻辑 tmp = vec[i]; j = i - gap; while(j >= 0 && vec[j] > tmp){vec[j+ gap] = vec[j]; j = j - gap;} vec[j + gap] = tmp; 注意:希尔排序是不稳定的

  5. 然后归并排序,把 vec 采用递归的思路,一分为二,然后把这两部分排序,拼装,返回。完成上一层的迭代语句,最后,就是一个完整的有序的vec 注意:归并是稳定的
    归并排序的思想,迭代、一分为二,一直细分最后只剩一个或两个元素 所以是一个二叉树的结构

  6. 快排,xxx

  7. 堆排,xxx

  8. 计数,统计元素出现次数,然后采用前缀和逻辑 返回output

  9. 桶,分桶再用稳定的排序对桶内元素排序,所以可以用插入排序,进而是稳定的。注意里也用到了前缀和逻辑

  10. 基数,比如都是两位数的元素,先按照个位排序,再按照十位排序,进而完成排序,适合整数、char类型排序,是稳定的排序,也用到了前缀和逻辑。


一点认识

计数排序的局限性很高,范围太大的排序(离散)、非整数的排序不是不符合要求的

本质是通过计数 比如 2 2 5 1 3 1 4 1 4 5
经过统计后 1 1 1 2 2 3 4 4 5 5
这样就是排序后的结果,只是 output[count[arr[i] - 1]] = arr[i]; count[arr[i]]–; 采用前缀和 的方式处理 比如 1 1 1 (待排序) ==> 1 1 (待排序)

最开始 采用 count 统计值 x 的个数 y , 储存方式 count[x] = y;  
把所有的 x1, x2, x3,...xn 储存,可以按照 min --> max 进行前缀求和 相同的元素(从右往左 依次削掉,就可以把前缀元素全部移入output)  

code

我的代码仓库(ten sorts code) 支持泛化排序, 不过特定的比如基数排序支持 char、int
动态排序 code
堆排序
快速排序


end

有几块要注意的,一个是由冒泡到选择。由选择到插入。 插入到希尔。 引入了 gap 归并 引入分治 堆、计数、基数、桶 引入分治 + 前缀和。注意:桶 是 分治再用线程的排序对桶内元素排序,所以稳定性就有保证


#c/c++