Java中基元数组的QuickSort vs MergeSort

时间:2017-03-13 01:10:02

标签: java arrays sorting quicksort mergesort

我知道Java的Arrays.sort方法使用MergeSort来排序对象数组(或对象集合),因为它是稳定的,Java使用QuickSort作为基元数组,因为我们不需要稳定性,因为两个相等的int是无法区分的,即他们的身份并不重要。

我的问题是,在原语的情况下,为什么Java不使用MergeSort的保证O(n log n)时间而是使用QuickSort的平均O(n log n)时间?在其中一个相关答案here的最后一段中,解释说:

  

对于引用类型,引用对象通常占用的内存远多于引用数组,这通常无关紧要。但对于原始类型,克隆数组会使内存使用量翻倍。

这是什么意思?克隆引用仍然至少与克隆基元一样昂贵。在原语数组上使用QuickSort(平均O(n log n))而不是MergeSort(保证O(n log n)时间)还有其他原因吗?

4 个答案:

答案 0 :(得分:4)

并非所有O(n log n)算法都具有相同的常数因子。 Quicksort,在99.9%的情况下,它需要n log n时间,运行速度比mergesort快得多。我不知道确切的乘数 - 它会改变系统的系统 - 但是,比如说,quicksort的运行速度可能是合并排序的两倍,并且仍然具有理论上的最差情况n ^ 2性能。

此外,Quicksort首先不需要克隆数组,并且合并排序不可避免。但是如果你想要一个稳定的排序,你没有选择参考类型,所以你必须接受副本,但你不需要接受基元的成本。

答案 1 :(得分:0)

Arrays#sort(primitive array)不使用传统的快速排序;它使用的是Dual-Pivot Quicksort,它比quicksort快,后者比合并排序更快,部分原因是它不必稳定。

来自javadoc:

  

实施说明:排序算法是Vladimir Yaroslavskiy,Jon Bentley和Joshua Bloch的双枢轴快速算法。该算法在许多数据集上提供O(n log(n))性能,导致其他快速排序降级为二次性能,并且通常比传统(单枢轴)Quicksort实现更快。

答案 2 :(得分:0)

  

克隆引用仍然至少与克隆原语一样昂贵。

Java的大多数(或所有?)实现将对象数组实现为对象的指针(引用)数组。因此,如果对象的大小比指针(引用)大,那么克隆一个指针数组(引用)将比克隆对象本身消耗更少的空间。

我不知道为什么这个词会克隆"被使用了。合并排序分配第二个临时数组,但该数组不是"克隆"原来的。相反,正确的合并排序会根据自下而上的迭代,或者根据自上而下的递归级别,将合并的方向从原始到临时或从临时到原始交替。

  

双枢轴快速排序

基于我可以发现进行网络搜索的内容,Java的双枢轴快速排序跟踪递归",并且如果递归深度过大则切换到堆排序,以维持O(n log(n))时间复杂度,但成本因素较高。

  

快速排序与合并排序

除了稳定性之外,合并排序可以更快地将对象(引用)数组排序到对象。合并排序会更多地移动(指针),但是(通过解除引用指针访问的对象)的比较比快速排序更少。

在具有16个寄存器(大多数用作指针)的系统上,例如64位模式下的X86,4路合并排序与常规快速排序一样快,但我不记得看到公共库中的4向合并排序,至少不适用于PC。

答案 3 :(得分:0)

  • 由于数据移动较少,QuickSort比随机数据上的MergeSort快约40%
  • QuickSort需要O(1)额外空间,而MergeSort需要O(n)

P.S。 Java标准库中既没有使用经典的QuickSort,也没有使用MergeSort。