这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
经典排序算法
插入排序
将元素不断插入已经排好序的array中
快速排序
采用分治的思想,不断分割序列,直到整体序列有序
- 选定一个pivot 轴点
- 使用pivot分割序列,分成元素比 pivot大和元素比 pivot小两个序列
堆排序
利用堆的性质形成的排序算法
- 构造一个大顶堆
- 将根节点(最大元素)交换到最后一个位置,调整整个堆,如此反复
总结
理论印象
- 插入排序平均和最坏情况时间复杂度都是O(n个2),性能不好
- 快速排序整体性能处于中间层次
- 堆排序性能稳定,“众生平等”
实际情况
- 插入排序在短序列中速度最快
- 快速排序在其他情况中速度最快
- 堆排序速度于最快算法差距不大
- 所有短序列和元素有序情况下,插入排序性能最好
- 在大部分的情况下,快速排序有较好的综合性能
- 几乎在任何情况下,堆排序的表现都比较稳定
pdqsort
v1
- 对于短序列(<=24)我们使用插入排序
- 其他情况,使用快速排序(选择首个元素作为pivot)来保证整体性能
- 当快速排序表现不佳时(limit==o),使用堆排序来保证最坏情况下时间复杂度仍然为O(n*logn)
- 短序列的具体长度是多少呢?
- 12~32,在不同语言和场景中会有不同,在泛型版本根据测试选定24
- 如何得知快速排序表现不佳,以及何时切换到堆排序?
- 当最终pivot的位置离序列两端很接近时(距离小于length/8)判定其表现不佳,当这种情况的次数达到 limit (即> bits.Len(length))时,切换到堆排序
v2
- 改进pivot
- 寻找pivot所需要的开销 平衡 pivot带来的性能优化
- 寻找近似中位数
- 优化-Pivot的选择
- 短序列(<=8),选择固定元素
- 中序列(<=50),采样三个元素,
- 长序列(>50),采样九个元素,
- 可以探知序列当前状态
- 逆序-> 翻转
- 有序-> 有限的插入排序
final
- 重复元素较多的情况(partitionEqual)
- 当检测到此时的pivot和上次相同时(发生在leftSubArray),使用partitionEqual将重复元素排列在一起,减少重复元素对于pivot选择的干扰
- 当pivot选择策略表现不佳时,随机交换元素
- 避免一些极端情况使得QuickSort总是表现不佳,以及一些黑客攻击情况