经典排序算法与改进算法 |青训营笔记

106 阅读2分钟

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

接下来我会对以下几方面进行总结:经典排序算法,最快的排序算法(Go 1.19)

经典排序算法

  1. 插入排序(Insertion Sortion) e.g.打牌

image.png

BestAvgWorst
O(n)O(n^2)O(n^2)
  1. 快速排序 (Quick sort)

    过程:选定一个pivot,分成两列(一列比pivot大,一列比pivot小)

    BestAvgWorst
    O(n*logn)O(n*logn)O(n^2)
  2. 堆排序 (Heap Sort) (时间复杂度稳定)

    过程: 大顶堆 root是最大值不断重复:

  • 提出root

  • 把末端数放入root

  • 重构建大顶堆

BestAvgWorst
O(n*logn)O(n*logn)O(n*logn)
  1. 实际场景中:

    | | 短序列(16) | 中序列(128) | 长序列(1024) | --- | ------- | --------------- ------------ | 完全随机情况 | 插入排序 | 快速排序| 快速排序 | sorted/reverse | 插入排序 | 插入排序 | 插入排序

结论:

  • 所有短序列和元素有序情况下,插入排序性能最好

  • 大部分情况,快速排序综合性能较好

  • 在任何情况下,堆排序表现稳定

改进的算法

  1. pdqsort(pattern-defeating-quicksort)

    是一种不稳定的(对于相同的值会有不同的排序)混合排序算法应用在Go 1.19中。使得在不同条件下拥有不错性能

  2. pdqsort v1

  • 短序列(12~32)使用插入排序

  • 其他情况使用快速排序保证整体性能

  • 快速排序性能不佳(当最终的pivot的位置离两端很接近(距离<len/8)判定不佳,这种次数达到limit,切换到堆排序)时,使用堆排序维持住最坏情况(保证最差时间复杂度为O(n*logn)

  1. pdqsort v2 (改进pivot的选择)
  • 方法一: 用首个元素作为pivot(若原本有序 达到最坏性能)

  • 方法二: 遍历数组,寻找真正的中位数 (遍历比对代价高)

  • 最好的方法: 寻找近似的中位数

    短序列(<=8),选择固定元素

    中序列(<=50),采样三个元素,选择三个元素的中位数作为中位数

    长序列(>50), 采样9个元素, 选择中位数

(通过采样我们有探知序列的能力:如果采样的元素是逆序,序列可能是逆序,翻转!;采样的元素为顺序排序,使用插入排序!)

  1. pdqsort final version (优化重复元素很多) 如果两次partition生成的pivot相同,partition进行了无效分割,pivot值为重复元素

当发现上述情况,使用partitionEqual将重复元素排列在一起,减少对pivot选择干扰

**下面是pdqsort的时间复杂度 **

BestAvgWorst
O(n)O(n*logn)O(n*logn)