经典排序算法
-
插入排序 Insertion Sort
- 将元素不断插入已经排序好的array中
- 时间按复杂度
- 最好 O(n) -有序
- 平均 O(n^2) -是翻转的对数决定的
- 最坏 O(n^2) -逆序
- 缺点:平均和最坏情况的时间复杂度高达O(n^2)
- 优点:最好情况的时间复杂度为O(n)
-
快速排序 Quick Sort
- 指定一个基准,分别从array的首尾找出比基准大、小的值
- 时间复杂度
- 最好 O(n*logn) -每一次刚好能分成平均的两边
- 平均 O(n*logn)
- 最坏 O(n^2)
- 缺点:最坏情况的时间复杂度高达O(n^2)
- 优点:平均情况的时间复杂度为O(nlogn)
-
堆排序 Heap Sort
- 有最大堆和最小堆之分
- 时间复杂度
- 最好 O(n*logn)
- 平均 O(n*logn)
- 最坏 O(n*logn)
- 缺点:最好情况的时间复杂度高达O(nlogn)
- 优点:最坏情况的时间复杂度为O(nlogn)
-
不同情况下的比较
- 理论
- 插入排序平均和最坏的时间复杂度都是O(n^2),性能不好
- 快速排序整体性能处于中间层次
- 堆排序性能稳定,“众生平等”
- 实际场景
- 完全随机的情况 -random
- 插入排序在短序列中速度最快
- 快速排序在其他情况中速度最快
- 堆排序速度与快速排序差距不大
- 有序情况 -sort
- 插入排序在有序情况下速度最快
- 结论
- 所有短序列和元素有序情况下,插入排序性能最好
- 在大部分情况下,快速排序有较好的综合性能
- 几乎在任何情况下,堆排序的表现都比较稳定
- 完全随机的情况 -random
- 理论
pdqsort - 一种不稳定的混合排序算法
-
version1
- 对于短序列(<=24) ,使用插入排序
- 其他情况,使用快速排序(选择首个元素作为 pivot)来保证整体性能
- 当快速排序表现不佳时 (limit==0),使用堆排序来保证最坏情况下时间复杂度仍然为 O(nxlogn)
-
version2 - 根据序列长度不同,来决定选择策略
- 优化-Pivot 的选择
- 短序列(<=8),选择固定元素
- 中序列(<=50),采样三个元素
- 长序列(>50),采样九个元素
- Pivot 的采样方式使得我们有探知序列当前状态的能力!
- 采样的元素都是逆序排列 - 序列可能已经逆序 - 反转整个序列
- 采样的元素都是顺序排列 - 序列可能已经有序 - 使用插入排序
- 注:插入排序实际使用partiallnsertionsort, 即有限制次数的插入排序
- 优化-Pivot 的选择
Version1 升级到 version2 优化总结: 升级 pivot 选择策略(近似中位数)
- 发现序列可能逆序,则翻转序列 ->应对reverse 场景
- 发现序列可能有序,使用有限插入排序->应对 sorted 场景
-
version3
- 元素重复度高
- 如果两次 partition 生成的 pivot 相同,即 partition 进行了无效分割,此时认为 pivot 的值为重复元素
- 当pivot 选择策略表现不佳时,
- 随机交换元素。避免一些极端情况使得 QuickSort 总是表现不佳,以及一些黑客攻击情况
- 元素重复度高
-
优化总结:
- 短序列情況:使用插入排序(v1)
- 极端情况:使用堆排序保证算法的可行性(v1)
- 完全随机的情况 (random):更好的pivot 选择策略(v2)
- 有序/逆序的情况 (sorted/reverse):根据序列状态翻转或者插入排序(v2)
- 元素重复度较高的情况 (mod8) -> v3
- pdqsort - 插入、快速、堆排序最好的结合
- 最好 O(n)
- 平均 O(nlogn)
- 最坏 O(nlogn)