我在一次面试中被问到这个问题。它们都是O(nlogn),但大多数人使用快速排序而不是归并排序。为什么呢?


当前回答

快速排序并不比归并排序好。对于O(n²)(很少发生的最坏情况),快速排序可能比归并排序的O(nlogn)慢得多。快速排序的开销更小,所以对于小n和速度较慢的计算机,它会更好。但是今天的计算机是如此之快,以至于合并排序的额外开销可以忽略不计,并且在大多数情况下,非常慢的快速排序的风险远远超过合并排序的微不足道的开销。

此外,归并排序将具有相同键的项按原始顺序保留,这是一个有用的属性。

其他回答

与归并排序不同,快速排序不使用辅助空间。而归并排序使用辅助空间O(n)。 归并排序的最坏情况时间复杂度是O(nlogn)而快速排序的最坏情况复杂度是O(n²)这发生在数组已经排序的时候。

这是一个相当老的问题,但因为我最近处理了这两个问题,所以这里是我的2c:

归并排序平均需要~ N log N次比较。对于已经(几乎)排序过的排序数组,这可以达到1/ 2nlog N,因为在归并时,我们(几乎)总是选择“左边”的1/ 2n次,然后只复制右边1/ 2n个元素。此外,我可以推测,已经排序的输入使处理器的分支预测器发光,但猜测几乎所有的分支都正确,从而防止管道停顿。

快速排序平均需要~ 1.38 nlog N个比较。在比较方面,它不会从已经排序的数组中获得很大的好处(但是在交换方面,可能在CPU内部的分支预测方面,它会获得很大的好处)。

我在相当现代的处理器上的基准测试显示如下:

当比较函数是回调函数时(如qsort() libc实现),对于随机输入,快速排序比归并排序慢15%,对于已经排序的64位整数,快排序比归并排序慢30%。

另一方面,如果比较不是回调,我的经验是快速排序优于归并排序高达25%。

然而,如果你的(大)数组只有很少的唯一值,归并排序在任何情况下都开始超过快速排序。

因此,底线可能是:如果比较是昂贵的(例如,回调函数,比较字符串,比较结构的许多部分,主要是得到第二个,第三个,第四个“if”来产生差异)-很可能你会更好地使用归并排序。对于简单的任务,快速排序会更快。

之前所说的都是真的: -快速排序可以是N^2,但Sedgewick声称,一个好的随机实现有更多的机会,计算机执行排序被闪电击中比N^2 —归并排序需要占用额外空间

实际上,快速排序是O(n2)。它的平均情况运行时间是O(nlog(n)),但最坏情况是O(n2),这发生在在包含很少唯一项的列表上运行时。随机化花费O(n)。当然,这并没有改变最坏的情况,它只是防止恶意用户使您的排序花费很长时间。

快速排序更受欢迎,因为它:

(MergeSort需要额外的内存,与要排序的元素数量成线性关系)。 有一个小的隐藏常数。

在归并排序中,一般算法为:

对左子数组进行排序 对右子数组进行排序 合并两个已排序的子数组

在顶层,合并两个已排序的子数组涉及处理N个元素。

再往下一层,第3步的每次迭代都涉及处理N/2个元素,但您必须重复此过程两次。所以你仍然在处理2 * N/2 == N个元素。

再往下一层,你要合并4 * N/4 == N个元素,以此类推。递归堆栈中的每个深度都涉及合并相同数量的元素,涉及对该深度的所有调用。

考虑一下快速排序算法:

选择一个枢轴点 将枢轴点放置在数组中的正确位置,所有较小的元素放在左边,较大的元素放在右边 对左子数组进行排序 对右子数组排序

在顶层,你处理的是一个大小为n的数组,然后选择一个枢轴点,把它放在正确的位置,然后可以在算法的其余部分完全忽略它。

再往下一层,您将处理2个子数组,它们的组合大小为N-1(即减去之前的枢轴点)。为每个子数组选择一个枢轴点,总共有2个额外的枢轴点。

再往下一层,您将处理4个子数组,它们的组合大小为N-3,原因与上面相同。

然后N-7…然后c15…然后N-32…

递归堆栈的深度保持大致相同(logN)。使用归并排序,你总是在递归堆栈的每一层处理n个元素的归并。但是使用快速排序,你要处理的元素数量会随着你在堆栈中向下移动而减少。例如,如果你在递归堆栈中查看深度,你正在处理的元素数量是N - 2^((logN)/2)) == N -根号(N)。

声明:对于归并排序,因为每次都将数组分割为两个完全相等的块,所以递归深度正好是logN。在快速排序时,由于枢轴点不太可能恰好位于数组的中间,因此递归堆栈的深度可能略大于logN。我还没有做过数学计算,看看这个因素和上面描述的因素在算法复杂性中究竟扮演了多大的角色。

在所有条件相同的情况下,我希望大多数人使用最方便的方法,这往往是qsort(3)。除此之外,快速排序在数组上非常快,就像归并排序是列表的常用选择一样。

我想知道的是为什么基数排序和桶排序这么少见。它们是O(n)至少在链表上是这样的它所需要的只是将键转换为序数的方法。(字符串和浮动工作得很好。)

我认为原因与计算机科学的教学方式有关。我甚至不得不向我的讲师演示算法分析,它确实有可能比O(nlog (n))更快地排序。(他证明了比较排序不能比O(nlog (n))快,这是正确的)

在其他新闻中,浮点数可以按整数排序,但之后必须将负数反转。

编辑: 实际上,这里有一种更糟糕的将浮点数作为整数排序的方法:http://www.stereopsis.com/radix.html。注意,不管你实际使用什么排序算法,比特翻转技巧都可以使用……