这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记
三种常用排序
理论复杂度:
| 最优 | 平均 | 最差 | |
|---|---|---|---|
| 插入排序 | O(N) | O(N^2) | O(N^2) |
| 快排 | O(NlogN) | O(NlogN) | O(N^2) |
| 堆排 | O(NlogN) | O(NlogN) | O(NlogN) |
插入排序:元素少的时候很快,相对有序的时候很快
快排:随机乱序下优秀,普遍情况下很快。逆序情况下退化为O(N^2)
堆排:稳定
随机序列情况下测试:
| 短序列 (~16) | 中序列 (~128) | 长序列 (~1024) | |
|---|---|---|---|
| 插入排序 | 最快 | 最慢 | 最慢 |
| 快排 | 最慢 | 最快 | 最快 |
| 堆排 | 中间 | 中间 | 中间 |
- 快排:短序列下快排递归导致的栈开销比排序本身开销更大
- 插入排序:短序列下插入排序的两重循环是非常快的
- 堆排:建堆操作在短序列情况下的开销也会大于排序本身
- 中长序列下复杂度符合理论复杂度分析,对于快排比堆排快的是因为快排使用分治+递归,复杂度约为O(NlogN),而堆排需要建堆和排序两个操作,那么实际的复杂度要在O(NlogN)前乘上系数2 。
有序情况下测试:
| 短序列 (~16) | 中序列 (~128) | 长序列 (~1024) | |
|---|---|---|---|
| 插入排序 | 最快 | 最快 | 最快 |
| 快排 | 最慢 | 最慢 | 最慢 |
| 堆排 | 中间 | 中间 | 中间 |
有序情况下插入排序是最优解,O(N) 复杂度。
而堆排序在有序情况下建堆操作的复杂度降为O(N),所以复杂度优于快排。
PdqSort
pdqSort 是基于快排的一个改进版本,进行了一些策略上的优化
| 最优 | 平均 | 最差 | |
|---|---|---|---|
| PdqSort | O(N) | O(NlogN) | O(NlogN) |
原理:插入排序、堆排、快排的综合使用
版本1:
- 元素个数小于等于 24 后转插入排序
- pivot 取首元素
- 左右大小相差很大时就是表现不佳转快排 (pivot 位置离两端很接近且次数达到 limit )
版本2:
-
pivot 取哪儿 - 根据长度不同决定选择策略
- 短(~10): 中值
- 中(~50): 三数选中法
- 长(>50): 九数选中法
-
pivot 采样方式使我们可探知当前状态
- 采样元素都是逆序 - 可能序列就是逆序 - 翻转
- 同理,推测正序情况直接插排
- 考虑误判和优化之间的平衡
- 插入排序使用限制次数的插入排序
- 如果误判,调整返回
-
重复度较高
- pivot 检测重复度,不好,效率不高
- 如果两次 partition 生产的 pivot 相同,即进行了无效分割,对重复元素进行处理
- 当 pivot 选择表现不佳, 在limit - 1 的同时,随机交换一些元素,避免极端情况和黑客恶意攻击