pdqsort学习 | 青训营笔记

107 阅读2分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 16 天,今天对go语言1.19中默认使用的pdqsort设计原理进行了简单的学习,并在这做一下小结

上一篇对基础排序算法总结的笔记:各基础排序算法小结

pdqsort简介

pdqsort,全称pattern-defeating-quicksort,是一种不稳定的混合排序算法,它的不同版本被用于C++ BOOST、RUST,它对常见的序列进行了特殊的优化,使得在不同情况下都拥有着不错的性能

pdqsort的初步设计及不断优化

version 1

借助上一篇总结,我们可以得到,在序列长度短时,我们可以使用插入排序;而中等长度以及较长的序列时,我们可以使用快速排序;当出现快速排序效率低时,我们可以转为使用各情况都比较稳定的堆排序,这样可以保证算法在最坏的情况下仍为O(nlogn)O(nlogn)的复杂度。

如何判断快排会出现效率低的情况

当选择的中心点最终位置离序列边界过近(距离<8),这样的次数超过limit次时,我们可以认定它表现不佳,切换为堆排序继续进行

version 2

对于version 1中的方法,我们还可以做些许改进,即改进快排的中心值选值方法,使选择的值的最终位置更靠近中心

一些寻找中心值方案

(1) 将第一个数作为中心值:确定中心值简单,但排序效率往往不好

(2) 寻找中位数作为中心值:确定中心值的消耗过大

为了兼顾寻找中心值的消耗,以及确认后排序的效率,需要采取寻找近似中位数

优化后的选择:

对于中序列(<=50),可以采样3个元素,将3个元素的中位数作为选中的中心值

对于长序列,可以采样9个元素,将9个元素的中位数作为选中的中心值(midian of midians)

对于采样出的元素序列,可以对原序列进行一定猜想:

采样的序列逆序 -> 原序列可能逆序 -> 翻转整个序列

采样的序列顺序 -> 原序列可能顺序 -> 使用有限制次数的插入排序

version 3

在优化完快排后,还有一种极端情况仍未被处理:序列元素重复度较高

因此,可以在选择中心值时,我们可以进行一些优化:当连续两次选择到相同的中心值时,可以将两个值排列到一块,减小相同值对快排效率的影响

其他优化

随机交换序列中的元素,既可以简单预防快排效率不佳,也可以简单防止黑客攻击

pdqsort复杂度分析

最优平均最差
O(n)O(n)O(nlogn)O(nlogn)O(nlogn)O(nlogn)

总结

要想设计一个性能较优的排序算法,需要利用各排序的优势,根据不同策略,取长补短;在初步设计出算法后,仍要继续思考有无尚未考虑的特殊情况,再对特殊情况进行考虑,这样才能把算法设计的更好。