更快的排序算法 | 青训营笔记

138 阅读1分钟

这是我参与「第三届青训营-后端场」笔记创作活动的的第11篇笔记。

经典排序算法

时间复杂度

最好平均最差
插入排序O(n)O(n)O(n2)O(n^2)O(n^2)
快速排序O(nlogn)O(n\log n)O(nlogn)O(n\log n)O(n2)O(n^2)
堆排序O(nlogn)O(n\log n)O(nlogn)O(n\log n)O(nlogn)O(n\log n)

实际 benchmark 结果

  • 短序列和元素有序情况下,插入排序性能最好
  • 大部分情况下,快速排序有较好的综合性能
  • 几乎在任何情况下,堆排序的表现都比较稳定

pdqsort

pdqsort(pattern-defeating-quicksort) 是一种不稳定的混合排序算法,它的不同版本被应用在 C++ boost,Rust 以及 Go 1.19 中。它对常见序列类型做了特殊优化,使得在不同条件下都拥有不错的性能。

结合前面提到的三种排序方法,提出的优化方案:

  • 对于短序列情况:使用插入排序
  • 其它情况使用快速排序
    • 优化 pivot 的选择:
      • 中序列(<=50)采样三个元素,使用 meidan of three;
      • 长序列(>50)采样九个元素,使用 median of median;
    • 采样发现序列可能逆序,翻转序列;可能有序,使用有限插入排序
    • 重复元素较多的情况(pivot 与上次相同时):使用 partitionEqual 将重复元素排列在一起
    • 当 pivot 选择策略表现不佳时,随即交换元素
  • 快速排序表现不佳(pivot离两端很近)达到一定次数时,切换到堆排序
最好平均最差
插入排序O(n)O(n)O(n2)O(n^2)O(n^2)
快速排序O(nlogn)O(n\log n)O(nlogn)O(n\log n)O(n2)O(n^2)
堆排序O(nlogn)O(n\log n)O(nlogn)O(n\log n)O(nlogn)O(n\log n)
pdqsortO(n)O(n)O(nlogn)O(n\log n)O(nlogn)O(n\log n)