什么时候使用每种分类算法?[关闭]


170

当使用特定的排序算法而不是其他排序算法时,有哪些用例?合并排序,快速排序,堆排序与“引入排序”等?

是否有根据大小,数据结构类型,可用内存和缓存以及CPU性能使用它们的推荐指南?


一组动画用于不同类型的数据和算法的可在<a href=" sorting-algorithms.com/">找到sorting-algorithms.com </一 >
芯片的Uni

2
类似的指南bigocheatsheet.com这个东西是greaaaat
的K -毒性SO越来越大。

@ChipUni这里是固定链接:toptal.com/developers/sorting-algorithms
eric

2
为什么这个问题不公开!
Arvand

Answers:


316

首先,一个定义,因为它非常重要:稳定排序是一种保证不会对具有相同键的元素重新排序的排序。

建议:

快速排序: 当您不需要稳定排序时,平均情况下的性能比最坏情况下的性能更重要。快速排序平均为O(N log N),在最坏的情况下为O(N ^ 2)。一个好的实现使用堆栈空间形式的O(log N)辅助存储进行递归。

合并排序: 当您需要稳定的O(N log N)排序时,这是您唯一的选择。唯一的缺点是它使用O(N)辅助空间,并且常数比快速排序大。有一些就地合并排序,但AFAIK不稳定或比O(N log N)差。就连O(N log N)排序中的常量也比普通的旧合并排序大得多,以至于它们比有用的算法更具理论上的好奇心。

堆排序: 当您不需要稳定的排序时,您更关心最差情况而不是平均情况。它保证为O(N log N),并使用O(1)辅助空间,这意味着您不会在非常大的输入上意外耗尽堆或堆栈空间。

Introsort: 这是一种快速排序,可以在经过一定的递归深度后切换到堆排序,以避开快速排序的O(N ^ 2)最坏的情况。它几乎总是比普通的旧式快速排序要好,因为您可以得到具有保证O(N log N)性能的快速排序的平均情况。可能使用堆排序而不是堆排序的唯一原因是在内存受严格限制的系统中,其中O(log N)堆栈空间实际上很重要。

插入排序:保证N较小时,包括作为快速排序或合并排序的基本情况。虽然这是O(N ^ 2),但它的常数很小,是稳定的排序。

气泡排序,选择排序:当您在进行快速而肮脏的操作时,由于某种原因,您不能只使用标准库的排序算法。这些优于插入排序的唯一优点是易于实现。


非比较排序: 在某些相当有限的条件下,有可能打破O(N log N)障碍并以O(N)进行排序。以下是一些值得尝试的情况:

计数排序: 对有限范围的整数进行排序时。

基数排序: 当log(N)明显大于K时,其中K是基数位数。

桶排序: 可以保证您的输入近似均匀地分布时。


1
我记得,堆排序的运行时间也非常可预测,因为相同大小的不同输入之间几乎没有变化,但是与其恒定的空间界限相比,它的兴趣不大。我还发现插入排序是n ^ 2排序中最容易实现的,但也许就是我自己。最后,您可能还想提到Shell sort,它几乎与插入排序一样简单实现,但性能更好,尽管仍然不是n log n。
JaakkoK,2009年

29
不要忘记Bogosort!;-)
Alex Brasetvik 09年

2
+1非常有趣。您是否愿意说明如何“保证...近似均匀地分布”。桶分类?
山姆·奥弗顿

2
为什么自省分类要比快速分类慢得多?唯一的开销是计算递归深度,这应该可以忽略不计。只有在递归的深度比快速排序的情况下要深得多时,它才会切换。
dsimcha's

2
您没有提及气泡排序的最佳情况是O(n)!
塔拉

33

通常,Quicksort通常是最快的,但是它有一些非常讨厌的最坏情况。因此,如果您必须保证没有不良数据提供给您O(N^2),则应避免这种情况。

合并排序使用了额外的内存,但是特别适合于外部排序(即,无法容纳在内存中的大文件)。

堆排序可以就地排序,并且没有最坏情况的二次行为,但是在大多数情况下,平均而言,堆排序要比快速排序慢。

如果只涉及有限范围内的整数,则可以使用某种基数排序使其变得非常快。

在99%的情况下,您通常会选择基于quicksort的库排序。


6
+1:对于“在99%的情况下,您会选择通常基于quicksort的库排序”。
Jim G.

随机数据透视为Quicksort提供了O(nlogn)的运行时间,可用于所有实际用途,而无需任何有关不良数据的保证。我真的不认为任何人都可以为任何生产代码实现O(n ^ 2)快速排序。
MAK

2
MAK,除了说C标准库qsort?(google.com/codesearch/...) -在大多数的“生产代码”各种各样的依靠
伊莱Bendersky

库排序通常不基于quicksort,因为它不稳定。几乎所有高级语言(期望C语言)都提供稳定的排序。在大多数情况下,我知道您需要一个稳定的或至少是确定性的排序。
12431234123412341234123



0

@dsimcha写道:计数排序:当您在有限范围内对整数进行排序时

我将其更改为:

计数排序:当您对正整数排序时(由于鸽子洞,0-Integer.MAX_VALUE-2)。

您也始终可以将最大值和最小值作为线性时间的效率试探法来获得。
另外,您至少需要n个额外的空间来放置中间数组,并且显然很稳定。

/**
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

(即使实际上允许使用MAX_VALUE-2),请参见: Java数组是否具有最大大小?

我还要解释一下,对于n个键(其字长为w的整数​​),基数排序复杂度为O(wn)。有时w被表示为一个常数,这将使基数排序(对于足够大的n而言)比基于最佳比较的最佳排序算法更好,后者均执行O(n log n)比较以对n个键进行排序。但是,通常不能将w视为常数:如果所有n个键都是不同的,则w必须至少为log n才能使随机存取机器能够将其存储在内存中,这充其量是最大的时间复杂度O (n log n)。(来自维基百科)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.