数据结构与算法-排序算法(下)|青训营笔记

112 阅读2分钟

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

从零实现pdqsort(Pattern-Defeating-QuickSort)

Version1

结合数据结构与算法-排序算法(上)|青训营笔记一文中插入排序,快速排序以及堆排序三种排序算法的优点

  • 对于短序列(小于一定长度),使用插入排序
  • 其他情况,使用快速排序保证整体性能
  • 当快速排序表现不佳时,使用堆排序来保证最坏情况下事件复杂度仍为O(nlogn)

短序列长度是多少

在go泛型版本中选定为24

如何得知快速排序表现不佳

使用快速排序每次会将数组分割成两个子数组,pivot的位置离序列两端很近(小于length/8)时,判定为表现不佳,当表现不佳次数超过一定阈值,切换至堆排序

总结

  • 对于短序列(<=24),使用插入排序
  • 其他情况,使用快速排序(选择首个元素为pivot)来保证整体性能
  • 当快速排序表现不佳,使用堆排序来保证最坏情况下时间复杂度仍能为O(nlogn)

Version2

pivot的选择

  • 使用首个元素作为pivot(最简单的方案) 实现简单,但数组在有序情况下,会达到最坏时间复杂度
  • 遍历数组,寻找真正的中位数 遍历比对代价高,性能不好

优化pivot的选择

  • 中序列(<=50),采样三个元素,取中位数,median of three
  • 长序列(>50),采样九个元素,median of medians 通过pivot的采样方式,可以探知序列当前的状态 当采样元素都是逆序,推断序列可能逆序,翻转整个序列 当采样元素都是顺序,推断序列可能有序,使用插入排序

总结

  • 升级pivot的选择策略(近似中位数)
  • 发现序列可能逆序,翻转序列
  • 发现序列可能有序,使用有限插入排序

final version

优化

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