排序算法 | 青训营笔记

96 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

经典排序算法

插入排序

将元素不断插入已经排好序的array中

image.png

快速排序

采用分治的思想,不断分割序列,直到整体序列有序

  • 选定一个pivot 轴点
  • 使用pivot分割序列,分成元素比 pivot大和元素比 pivot小两个序列

image.png

堆排序

利用堆的性质形成的排序算法

  • 构造一个大顶堆
  • 将根节点(最大元素)交换到最后一个位置,调整整个堆,如此反复

总结

image.png

理论印象

  • 插入排序平均和最坏情况时间复杂度都是O(n个2),性能不好
  • 快速排序整体性能处于中间层次
  • 堆排序性能稳定,“众生平等”

实际情况

  • 插入排序在短序列中速度最快
  • 快速排序在其他情况中速度最快
  • 堆排序速度于最快算法差距不大
  • 所有短序列和元素有序情况下,插入排序性能最好
  • 在大部分的情况下,快速排序有较好的综合性能
  • 几乎在任何情况下,堆排序的表现都比较稳定

pdqsort

v1

  • 对于短序列(<=24)我们使用插入排序
  • 其他情况,使用快速排序(选择首个元素作为pivot)来保证整体性能
  • 当快速排序表现不佳时(limit==o),使用堆排序来保证最坏情况下时间复杂度仍然为O(n*logn)
  1. 短序列的具体长度是多少呢?
    • 12~32,在不同语言和场景中会有不同,在泛型版本根据测试选定24
  2. 如何得知快速排序表现不佳,以及何时切换到堆排序?
    • 当最终pivot的位置离序列两端很接近时(距离小于length/8)判定其表现不佳,当这种情况的次数达到 limit (即> bits.Len(length))时,切换到堆排序

v2

  • 改进pivot
    • 寻找pivot所需要的开销 平衡 pivot带来的性能优化
    • 寻找近似中位数
  • 优化-Pivot的选择
    • 短序列(<=8),选择固定元素
    • 中序列(<=50),采样三个元素,
    • 长序列(>50),采样九个元素,
  • 可以探知序列当前状态
    • 逆序-> 翻转
    • 有序-> 有限的插入排序

final

  • 重复元素较多的情况(partitionEqual)
    • 当检测到此时的pivot和上次相同时(发生在leftSubArray),使用partitionEqual将重复元素排列在一起,减少重复元素对于pivot选择的干扰
  • 当pivot选择策略表现不佳时,随机交换元素
    • 避免一些极端情况使得QuickSort总是表现不佳,以及一些黑客攻击情况

image.png