快排与堆排序、归并排序的比较

 
  

数据结构与算法分析中的描述:

与归并排序相似,快速排序递归的解决两个子问题并需要线性的附加工作,但两个子问题大小不等带来了性能的潜在隐患。之所以更快是因为在按照枢轴分割为两组时,实际上是在适当位置进行并且非常有效,它的高效弥补了大小不等的递归调用的缺憾并且有所超出。但是,归并排序的比较次数是最优的。

归并排序的性能对于主存排序不如快速排序好,而且它的编程也不省事。


在实际应用中,快排性能优于堆排序,其O(nlogn)隐藏的常数因子项小,它可以进行原址排序,在虚存环境下也能很好的工作。

另外,堆排序在实践中甚至慢过sedgewick希尔排序。


算法导论:关于基数排序与快排等的比较---P111


虽然归并会做几乎完美的分割,效果较快排好,但是多计N*LongN,归并反而慢。

好像要比快排多花40%的时间

归并理论上效率很好,但是在while循环时,要判断是否出界,多做n*logn次比较,还要把数据拷贝回去,速度明显慢于快排。

我们刚才讨论归并理论上更快,就是指归并可以把序列分成两个几乎相等的子序列,无论怎样的快排算法都无法保证选取枢纽元后
序列被分成两个相等的序列,所以就分割而言,快排不如归并,导致其递归次数也会多于归并。

归并排序在最坏情况下键值的比较次数十分接近于任何基于比较的排序算法在理论上能够达到的最少次数(nlogn - 1.44n),虽然归并也能做到“在位”,但是会导致算法过于复杂,而且,因为它的增长次数具有一个很大的常系数,所以在位的归并排序算法只有理论上的意义。


另一个角度是从快速排序怎么优化的得到它能更好的结论:

1) 利用存储子任务的栈来消除递归

2) 利用基于三中值分区的中枢值

3) 设定一个使用切分时数组长度的最小值,如果小于这个值,就使用插入排序(这个最小值根据经验给定,一般设定为4或者5)

4) 当处理子数组的时候,首先将大的子数组压入栈中,这样可以最小化栈的总大小,确保小的问题首先被解决

根据这四点可以论证快排比堆排序好:

由上述代码可知,堆排序的过程就是n次建堆的过程,

总比较次数小于2nlogn  (以2为底),即比较次数比普通快排少。

建堆的代码已经相当简练,优化带来的效率提高有限。

因为不涉及到切分问题,所以能加快速度的 第3点只能用1次,而在快排中可以用多次。

堆排序是循环而非递归过程,不存在显式栈问题,而且这个循环调用函数引起了堆的变化,所以不可能提到循环外面。

循环展开和多路并发方面也不如 快排,(快排是适用分治法的,各个子问题相互独立)。

综上,快速排序最终肯定能完爆堆排序。


比较这三个排序还进一步说明了:算法复杂度和程序的运行快慢是两个不同的概念,前者只是影响后者的一个因素

算法复杂度一样只是说明随着数据量的增加,算法时间代价增长的趋势相同,并不是执行的时间就一样,这里面有很多常量参数的差别,即使是同样的算法,不同的人写的代码,不同的应用场景下执行时间也可能差别很大。

快排的最坏时间虽然复杂度高,但是在统计意义上,这种数据出现的概率极小,而堆排序过程里的交换跟快排过程里的交换虽然都是常量时间,但是常量时间差很多。这句话可以结合代码深入分析一下,堆排序交换数据是不断将末尾元素交换至头部,然后再对除最后一个元素的n-1个元素调整为大顶堆,这会带来程序运行时更频繁的换页行为;相比之下,快排基于分治,在递归过程不断划分为较小区间的求解,所以数据交换带来的换页行为并不如堆排序频繁。当然,若递归深度太大的情况下快排会出现恶化的情况,这时要转为调用堆排序,STL的sort策略就是这样的。


还有基数排序与快速排序的性能比较又如何呢?

基数排序的缺点是不呈现时空局部性,因为在按位对每个数进行排序的过程中,一个数的位置可能发生巨大的变化,所以不能充分利用现代机器缓存提供的优势。同时计数排序作为中间稳定排序的话,不具有原地排序的特点,当内存容量比较宝贵的时候,还是有待商榷。另外基数排序不是基于比较的,而基于比较的排序最好也就是O(nlogn),所以:

  1. 基于比较的排序,有个隐含的假定:关键字比较的开销为O(1)。
  2. 对长度为n的数组排序,如果数组的 key 都不相同,那么根据信息论,每个关键字的长度是log2(n)。
  3. 分析时间复杂度时考虑以上两点,那么基数排序可以认为是在O(1)的时间内做了分支数为Radix的决策。而基于比较的排序需要log2(Radix)次决策。

参考:http://bbs.chinaunix.net/thread-1748866-1-1.html

http://blog.csdn.net/Hackbuteer1/article/details/6568913

你可能感兴趣的:(数据结构与算法)