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


当前回答

亩! 快速排序并不比归并排序更好,它非常适合于不同类型的应用。

归并排序是值得考虑的,如果速度是本质,糟糕的最差情况性能不能容忍,并且有额外的空间可用

你说他们«他们都是O(nlogn)[…]»。这是错误的。«快速排序使用大约n^2/2比较在最坏的情况下。

然而,根据我的经验,最重要的属性是在使用带有命令式范式的编程语言进行排序时,可以轻松实现顺序访问。

1 Sedgewick,算法

其他回答

正如许多人所注意到的,快速排序的平均情况性能要比归并排序快。但这只适用于假设按需访问任何内存段的时间为常数的情况。

在RAM中,这种假设通常不太坏(由于缓存的存在,这种假设并不总是正确的,但也不太坏)。然而,如果你的数据结构足够大,可以存储在磁盘上,那么快速排序就会因为磁盘平均每秒进行200次随机查找而被扼杀。但是,同样的磁盘在按顺序每秒读取或写入兆字节的数据方面没有任何问题。这正是归并排序所做的。

因此,如果数据必须在磁盘上排序,你真的,真的想使用归并排序的一些变体。(通常你快速排序子列表,然后开始将它们合并到某个大小阈值以上。)

Furthermore if you have to do anything with datasets of that size, think hard about how to avoid seeks to disk. For instance this is why it is standard advice that you drop indexes before doing large data loads in databases, and then rebuild the index later. Maintaining the index during the load means constantly seeking to disk. By contrast if you drop the indexes, then the database can rebuild the index by first sorting the information to be dealt with (using a mergesort of course!) and then loading it into a BTREE datastructure for the index. (BTREEs are naturally kept in order, so you can load one from a sorted dataset with few seeks to disk.)

在许多情况下,了解如何避免磁盘寻道使我将数据处理工作花费数小时而不是数天或数周。

亩! 快速排序并不比归并排序更好,它非常适合于不同类型的应用。

归并排序是值得考虑的,如果速度是本质,糟糕的最差情况性能不能容忍,并且有额外的空间可用

你说他们«他们都是O(nlogn)[…]»。这是错误的。«快速排序使用大约n^2/2比较在最坏的情况下。

然而,根据我的经验,最重要的属性是在使用带有命令式范式的编程语言进行排序时,可以轻松实现顺序访问。

1 Sedgewick,算法

快速排序是一种就地排序算法,因此它更适合于数组。另一方面,归并排序需要额外的O(N)存储空间,更适合于链表。

与数组不同,在喜欢列表中,我们可以在中间插入O(1)空间和O(1)时间的项,因此归并排序中的归并操作可以在不需要任何额外空间的情况下实现。但是,为数组分配和取消分配额外空间会对归并排序的运行时间产生不利影响。归并排序也有利于链表,因为数据是按顺序访问的,没有太多的随机内存访问。

另一方面,快速排序需要大量的随机内存访问,而使用数组,我们可以直接访问内存,而不需要像链表那样进行任何遍历。同样,快速排序用于数组时具有良好的引用局部性,因为数组连续存储在内存中。

尽管这两种排序算法的平均复杂度都是O(NlogN),但通常人们在执行普通任务时使用数组进行存储,因此快速排序应该是首选算法。

编辑:我刚刚发现归并排序最差/最好/平均情况总是nlogn,但快速排序可以从n2(最坏的情况下,元素已经排序)到nlogn(平均/最佳情况下,pivot总是将数组分为两半)。

正如其他人所注意到的,快速排序的最坏情况是O(n²),而归并排序和堆排序则停留在O(nlogn)。然而,在平均情况下,这三个都是O(nlogn);所以它们在大多数情况下是可比较的。

平均而言,快速排序更好的地方在于,内循环意味着将多个值与单个值进行比较,而在其他两个循环中,每次比较时两个项都是不同的。换句话说,Quicksort的读取次数是其他两种算法的一半。在现代cpu上,访问时间在很大程度上决定了性能,因此快速排序最终成为一个很好的首选。

这是一个相当老的问题,但因为我最近处理了这两个问题,所以这里是我的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 —归并排序需要占用额外空间