快速排序和堆排序

Posted 幽流书堂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序和堆排序相关的知识,希望对你有一定的参考价值。

曲径通幽,隰有苌楚。夹岸百里,婀娜其实。 每周学习一个算法。


本篇文章是货真价实的干货!这次对比的不是选择排序和堆排序,而是快速排序和堆排序,因为选择排序实在没有什么好对比的,怎么排怎么慢。这次没有运行结果的对比,都是理论性的内容。另外我没有给这篇文章加小标题,是因为我希望读者把每一段都认真看一看,而不是通过标题来揣测段落大意。




先前说过,排大数组通常选用快排,因为快。不选择代码复杂度稍小一点的希尔排序的原因是因为步长序列比较难掌握,因为要考虑让奇偶(下标互质)项在最后一轮循环前都尽可能多地互相比较。快排的时间复杂度为 O(nlog2n) ~ O(n2),而希尔排序的时间复杂度为 O(n1.3) ~ O(n2)。在实际应用过程中,快排的最差情况不太容易遇到,但希尔排序并不容易找到较好的步长序列。所以快排会略快一些。不使用堆排序是因为通常堆排序会稍稍慢一点,稍后将详细解释。


快排速度快还有其他原因。快排的思路类似于二分法。二分法的操作步骤十分机械,但优点是,无论如何都能排除掉一半的错误答案。快排每轮递归前都将当前数组分成两部分,进入递归时就排除掉了很多不相干的项而不去比较,更谈不上交换,从而节省了时间。




上文提到堆排序通常要比快排慢一点。从算法来看,堆排序在排序之前要构建堆结构,每次选择后还要整理堆结构,花费了很多时间。仔细分析堆排序的过程,我们会发现每次首尾节点交换后,都会使一个很小的值位于根节点,而这个根节点的值在我们的直觉上根本不可能会比两个叶子节点更大。事实上只要不是特别极端的数组,一定会小于两个叶子节点的最大值,也不太可能会大于等于另一个值,这就使排序做了一些无用功。或者我们从元素的交换移动来理解,快排中每次进入递归后,元素交换大多在初始位置附近操作,并逐步越来越接近最终的正确位置,而堆排序中较小的值则大幅度且频繁跳跃,比如值为“1”的节点反复出现在根节点的正确位置却又反复移动到最深的叶子节点。




最后,我们要提一提快排的局限性。快排存在最差情况,届时的时间复杂度为 O(n2),递归树为一个线性结构,比如给逆序数组排序。而快排的最好情况就是其所形成的递归树是完全二叉树的情况,显然这种情况同逆序数组一样很难遇到,快排的局限性就来自于此。在进入递归前,当前数组并不一定能被近似等分为两个数组,这就导致递归结果不是很符合二分法,也就意味着元素个数多的那一半可能要多进行一次或几次递归,而少的那一半相反。进入递归时分得越不平均,递归树就越接近线性结构,时间复杂度就越接近 O(n2),所以快排有时候并没有预想的那么快。

以上是关于快速排序和堆排序的主要内容,如果未能解决你的问题,请参考以下文章

递归:快速排序,归并排序和堆排序

数据结构28 | 堆和堆排序:为什么说堆排序没有快速排序快?

On the March-附快速排序和堆排序代码

十种常见排序算法

快速排序详解与各种线性时间排序对比

七大排序之:直接选择排序和堆排序