排序算法实践 | 青训营笔记

115 阅读3分钟

经典排序算法

  1. 插入排序 Insertion Sort

    • 将元素不断插入已经排序好的array中
    • 时间按复杂度
      • 最好 O(n) -有序
      • 平均 O(n^2) -是翻转的对数决定的
      • 最坏 O(n^2) -逆序
    • 缺点:平均和最坏情况的时间复杂度高达O(n^2)
    • 优点:最好情况的时间复杂度为O(n)
  2. 快速排序 Quick Sort

    • 指定一个基准,分别从array的首尾找出比基准大、小的值
    • 时间复杂度
      • 最好 O(n*logn) -每一次刚好能分成平均的两边
      • 平均 O(n*logn)
      • 最坏 O(n^2)
    • 缺点:最坏情况的时间复杂度高达O(n^2)
    • 优点:平均情况的时间复杂度为O(nlogn)
  3. 堆排序 Heap Sort

    • 有最大堆和最小堆之分
    • 时间复杂度
      • 最好 O(n*logn)
      • 平均 O(n*logn)
      • 最坏 O(n*logn)
    • 缺点:最好情况的时间复杂度高达O(nlogn)
    • 优点:最坏情况的时间复杂度为O(nlogn)
  4. 不同情况下的比较

    1. 理论
      • 插入排序平均和最坏的时间复杂度都是O(n^2),性能不好
      • 快速排序整体性能处于中间层次
      • 堆排序性能稳定,“众生平等”
    2. 实际场景
      1. 完全随机的情况 -random
        • 插入排序在短序列中速度最快
        • 快速排序在其他情况中速度最快
        • 堆排序速度与快速排序差距不大
      2. 有序情况 -sort
        • 插入排序在有序情况下速度最快
      3. 结论
        • 所有短序列和元素有序情况下,插入排序性能最好
        • 在大部分情况下,快速排序有较好的综合性能
        • 几乎在任何情况下,堆排序的表现都比较稳定

pdqsort - 一种不稳定的混合排序算法

  1. version1

    • 对于短序列(<=24) ,使用插入排序
    • 其他情况,使用快速排序(选择首个元素作为 pivot)来保证整体性能
    • 当快速排序表现不佳时 (limit==0),使用堆排序来保证最坏情况下时间复杂度仍然为 O(nxlogn)
  2. version2 - 根据序列长度不同,来决定选择策略

    • 优化-Pivot 的选择
      • 短序列(<=8),选择固定元素
      • 中序列(<=50),采样三个元素
      • 长序列(>50),采样九个元素
    • Pivot 的采样方式使得我们有探知序列当前状态的能力!
      • 采样的元素都是逆序排列 - 序列可能已经逆序 - 反转整个序列
      • 采样的元素都是顺序排列 - 序列可能已经有序 - 使用插入排序
    • 注:插入排序实际使用partiallnsertionsort, 即有限制次数的插入排序

Version1 升级到 version2 优化总结: 升级 pivot 选择策略(近似中位数)

  • 发现序列可能逆序,则翻转序列 ->应对reverse 场景
  • 发现序列可能有序,使用有限插入排序->应对 sorted 场景
  1. version3

    • 元素重复度高
      • 如果两次 partition 生成的 pivot 相同,即 partition 进行了无效分割,此时认为 pivot 的值为重复元素
    • 当pivot 选择策略表现不佳时,
      • 随机交换元素。避免一些极端情况使得 QuickSort 总是表现不佳,以及一些黑客攻击情况
  2. 优化总结:

  • 短序列情況:使用插入排序(v1)
  • 极端情况:使用堆排序保证算法的可行性(v1)
  • 完全随机的情况 (random):更好的pivot 选择策略(v2)
  • 有序/逆序的情况 (sorted/reverse):根据序列状态翻转或者插入排序(v2)
  • 元素重复度较高的情况 (mod8) -> v3
  • pdqsort - 插入、快速、堆排序最好的结合
    • 最好 O(n)
    • 平均 O(nlogn)
    • 最坏 O(nlogn)