CS61B笔记 | Sorting
选择排序
- 扫描整个数组找出最小的元素
- 交换到已排序部分的末尾
- 循环往复
时间复杂度O($N^2$)
一遍一遍找最小项
堆
- 数组元素依次添加进最大堆 O($NlogN$)
- 从堆中取元素进入新的数组 O($NlogN$)
总共的时间复杂度O($NlogN$),这比选择排序要好。空间复杂度O(N),因为有两个额外数组。
原地堆排序
- 一个数组,它不是堆,但我们假装它是堆,从堆的底部出发,让每一个元素下沉。在下沉了所有元素之后,排序也就完成了。
- 使用最大堆
总共的时间复杂度O($NlogN$),但是摆脱了所有额外内存,空间复杂度为O(1)
归并排序
如果有两个小数组,如何得到一个大的数组?
- 对选择排序的一个改进:把它分成两半,对每一半进行选择排序,然后再选择排序合并回来。
- 如果这两半再分裂成两半呢?我们需要两次合并。
归并排序的核心思想是将数组分为两半,递归地对每一半进行归并排序,最后将两部分合并成一个有序的数组。
时间复杂度O($NlogN$)
如果有两个小数组,如何得到一个大的数组
归并排序是所有这些性能最稳定的算法。
插入排序
- 类似于玩扑克牌时的整理方式,即逐个将牌插入到已排序的部分中。插入排序对于已经部分排序的数据有很好的性能,具有稳定性,且实现起来相对简单。
时间复杂度O($N^2$)
旅行操作,在那里找出元素需要去哪里 - 如果是一个有序数组,更改了其中某一个元素,重新排序时选择插入排序将会是最快的!
- 如果数组非常小,插入排序将会是最快的!(如果数组大小约为15或者更小,插入排序快于归并排序)
- 在JAVA中,归并排序的实现是分割数组到足够小(并不会到1),直接进行插入排序。结果证明,在现实生活中它更快。
快速排序
Hello 算法对快速排序的介绍
复杂度:
- 最优情况下时间复杂度 O($NlogN$)
- 在处理已排序数组时,出现最坏时间复杂度 O($N^2$)
- 给一个随机数组,O($NlogN$)的概率极高
关键操作:分区
二叉搜索树的中序遍历和快速排序,其实是相同的。
快速排序速度比归并排序更快
如何规避 O($N^2$)复杂度?
- 提前检查数组是否有序,如果有序丢给归并排序或者插入排序。
- 打乱数组重新排序。
- 随机选取基准值。
复杂度数学分析
$NlogN$ = $logN!$ 可以把他们看成相等的。
结论:堆排序、归并排序、快速排序都是最快的排序,找不到比他们更快的了!(从渐进分析的角度来看) 仅适用于比较算法
基数排序/逐位排序
先按个位排序,再按十位排序,再按百位排序
MSD最高有效位优先
LSD最低有效位优先
计数排序
如果不进行比较,那么就能规避O($NlogN$)时间复杂度上限。
非基于比较的排序是根据键值本身的定位特征来决定位置的,因此就不需要根据其键值的比较来决定其前后位置关系。
计数排序的时间复杂度是O(n+k),其中n是待排数列长度,而k则是对应进制的基数。
b站视频链接
计数排序可视化