ᕕ( ᐛ )ᕗ xiaoli's blog

【go】pdqsort

; 2118 words  

目录

1.排序算法

语言 排序算法
python timsort
go1.18 混合排序,主要是快速排序
rust pdqsort
c++ intro sort

1. 插入排序

将元素不断插入已排序的序列中。即不断交换,直到找到第一个比其小的元素。

2. 堆排序

利用堆的性质,构造一个大顶堆,将最大元素交换到最后一个位置,调整整个堆,如此反复

3. 快速排序

递归,不断分割序列直到序列整体有序。选定一个pivot,使用pivot分割序列,分成元素比pivot大和元素比pivot小的两个序列。
当pivot为中点,快排表现最好;当pivot选择为最大/最小元素时,快排表现最差。

实际场景下,根据序列长度划分后进行benchmark(16/128/1024)

2. pattern-defeating-quicksort

该算法结合三种排序算法的优点:

短序列的长度为多少?12-32,在go语言的实现版本中,将短序列的长度选定为12。
何时从快排切换到堆排序?最终pivot距离序列两端距离小于length/8判定为快排表现不佳,这种情况次数达到bits.Len(length)时,切换为堆排序。bits.Len(length)表达的是一种阈值。

1. 关键点

2. 函数解析

1. choosePivot:选择pivot

支持三种选择方法:静态选择、三数取中、Tukey’s ninther
当数组长度大于等于 8 时,选择1/4、2/4、3/4处,在这三个位置中再找一个中位数作为最终的枢轴 如果长度大于等于 50 时,选择1/4、2/4、3/4处及其每处的相邻元素(共9个元素)的中位数作为最终的枢轴

swaps:寻找中位数的过程中的交换次数

2. partialInsertionSort:局部插入排序

当选择的pivot递增时,原始序列可能也递增,此时需要对序列进行局部插入排序。 该插入排序最多处理5对乱序元素,遍历序列发现乱序元素后,同时向两边移动乱序元素。 返回值含义:

3. partitionEqual: 处理大量重复元素的情况

a > 0 && !data.Less(a-1, pivot)的条件保证了此次数组中不存在小于枢轴值的元素,首次执行时a==0,不会执行该逻辑。
将枢轴移动到数组开头,找到第一个大于和等于枢轴的元素,交换位置。
如[3, 3, 3, 3, 5, 5, 3, 6, 3],pivot = 3,运行后变为:[3 | 3, 3, 3, 3, 3, 3 | 5, 5, 6],返回值 i 指向第一个大于3的位置

4. partition:根据pivot进行排序

将数组分成两部分:小于枢轴值的元素和大于等于枢轴值的元素,和一般的快排中partition本质一致。
在分区前会进行一个提前检查,若扫描发现原始数组已分区,则无需再次分区,alreadyPartitioned返回true。

5. breakPatterns:当分区不平衡时,打乱数据

通过随机交换中间位置的元素来打破数组中可能导致快速排序分区不平衡的模式 只处理中间位置附近的三个元素,使用xorshift 随机数生成器

3. 变量解析

变量名 含义
maxInsertion 当前序列长度小于maxInsertion时,使用插入排序
limit 当limit等于0时,使用堆排序保证排序性能,初始值为bits.Len(uint(n))
wasBalanced 通过比较左右子数组长度和阈值(len/8)来判断上一次partition是否平衡,如果不平衡,会触发breakPatterns来打乱数据
wasPartitioned 表示数组是否已经被分好区,即数组接近有序

4. 总结

本质上pdqsort是结合三种常用排序的优缺点的混合排序方式,基于benchmark编码,根据场景判断后选择合适的排序方式。

5.参考

https://talkgo.org/t/topic/4342
https://mp.weixin.qq.com/s/5HqfRGqPyAhFt0krPgMHOQ

This is a page about »PdqSort«.

#go #排序算法