为啥 Collections.sort 使用合并排序而不是快速排序?

Posted

技术标签:

【中文标题】为啥 Collections.sort 使用合并排序而不是快速排序?【英文标题】:Why Collections.sort uses merge sort instead of quicksort?为什么 Collections.sort 使用合并排序而不是快速排序? 【发布时间】:2013-02-15 17:50:14 【问题描述】:

我们知道快速排序是最快的排序算法。

JDK6 collections.sort 使用归并排序算法而不是快速排序。但是 Arrays.sort 使用的是快速排序算法。

Collections.sort 使用合并排序而不是快速排序的原因是什么?

【问题讨论】:

除非您能得到 JDK 作者的回答,否则您将得到的只是猜测。不是一个真正的问题。 @EJP 好点,但肯定“非建设性”是正确的关闭原因。我很清楚这里的问题是什么。 因为 Java 人决定这样做。问问他们。我认为您无法在这里得到合理的答案。快速排序不是最好的。它只适合通用 一种猜测:Quicksort 不稳定,Mergesort 是。对于原语,稳定/不稳定排序是无关紧要的,对于对象它可能是(或者至少,您可能会收到针对不稳定排序的错误)。 @EJP,没有什么可以阻止 JDK 作者的意图公开。一旦公开,我们就不需要作者本人来回答了。事实上,即使没有 JDK 作者的回答,也有可能得到超出猜测的答案。 【参考方案1】:

Josh Bloch 很有可能§:

我确实写了这些方法,所以我想我有资格回答。它是 确实没有单一的最佳排序算法。快速排序有 与归并排序相比有两个主要缺陷:

    不稳定(如 parsifal 所述)。

    它不保证 n log n 性能;它可以在病理输入上降级为二次性能。

稳定性对于原始类型来说不是问题,因为没有 身份不同于(价值)平等。并且有可能 二次行为在实践中被认为不是问题 Bentely 和 McIlroy 的实现(或随后用于 Dual Pivot Quicksort),这就是为什么这些 QuickSort 变体被用于 原始排序。

对任意对象进行排序时,稳定性很重要。例如, 假设您有代表电子邮件的对象,并且您排序 他们首先按日期,然后按发件人。您希望它们按以下顺序排序 每个发件人中的日期,但只有在排序为 稳定的。这就是我们选择提供稳定排序(Merge Sort)的原因 对对象引用进行排序。 (从技术上讲,多个顺序 稳定的排序导致在 排序的倒序:最后的排序决定了最 重要的子键。)

Merge Sort 保证 n log n(时间)是一个很好的附带好处 无论输入什么性能。当然也有不好的一面: 快速排序是一种“就地”排序:它只需要 log n 个外部空间 (维护调用堆栈)。另一方面,合并,排序, 需要 O(n) 外部空间。 TimSort 变体(在 Java 中引入 SE 6) 如果输入数组是 几乎排序。

另外,following 是相关的:

java.util.Arrays.sort 和(间接)使用的算法 java.util.Collections.sort 对对象引用进行排序是“修改 合并排序(如果合并中的最高元素,则省略合并 低子列表小于高子列表中的最低元素)。”它 是一种相当快速的稳定排序,保证 O(n log n) 性能并需要 O(n) 额外空间。在它的时代(它被写成 1997 年 Joshua Bloch),这是一个不错的选择,但今天我们可以 做得更好。

自 2003 年以来,Python 的列表排序使用了一种称为 timsort 的算法 (在编写它的蒂姆彼得斯之后)。它是一个稳定的、自适应的、迭代的 需要远少于 n 次 log(n) 比较的合并排序 在部分排序的数组上运行,同时提供性能 在随机数组上运行时可与传统的归并排序相媲美。喜欢 所有正确的归并排序 timsort 都是稳定的并且在 O(n log n) 时间内运行 (最差的情况)。在最坏的情况下,timsort 需要临时存储 n/2 个对象引用的空间;在最好的情况下,它只需要一个 小的恒定空间。与当前的对比 实现,它总是需要为 n 个对象提供额外空间 参考文献,并且仅在几乎排序的列表上击败 n log n。

Timsort 在这里详细描述: http://svn.python.org/projects/python/trunk/Objects/listsort.txt.

Tim Peters 的原始实现是用 C. Joshua Bloch 编写的 将其从 C 移植到 Java,并进行了最终测试、基准测试和调优 产生的代码广泛。生成的代码是一个插件 替换 java.util.Arrays.sort。在高度有序的数据上,这 代码的运行速度可以达到当前实现的 25 倍(在 HotSpot 服务器虚拟机)。在随机数据上,新旧速度 实现具有可比性。对于非常短的列表,新的 即使是随机的,实现也比旧的快得多 数据(因为它避免了不必要的数据复制)。

另外,请参阅Is Java 7 using Tim Sort for the Method Arrays.Sort?。

没有一个“最佳”选择。与许多其他事情一样,它是关于权衡的。

【讨论】:

以上是关于为啥 Collections.sort 使用合并排序而不是快速排序?的主要内容,如果未能解决你的问题,请参考以下文章

面试必备常见Java面试题大综合

常见Java面试题大综合,面试必备

为啥我不能在我的 ArrayList<T> 上调用 Collections.sort()?

「面试必备」常见Java面试题大综合 马云见了都点赞

为啥 collections.sort 在 Java 中按比较器排序时会抛出不支持的操作异常?

comparable接口;collections类;