哪种排序算法最适合大多数排序的数据?[关闭]


175

哪种排序算法最适合大多数排序的数据?


由于缺乏上下文而进行猜测-您是否在询问是否需要将中间结果溢出到磁盘的内存中排序?
乔纳森·勒夫勒

1
根据这些动画,插入排序对大多数排序数据最有效。
dopple 2012年

Answers:


260

基于观看动画gif的高度科学的方法,我会说插入和冒泡排序是不错的选择。


19
顺便说一句,这是一个很好的链接,荣誉和+1
双面

5
气泡排序很糟糕。总是O(n ^ 2)。至少请从您的答案中删除该内容,以确保正确。
jjnguy

80
jjnguy,那完全是错误的。我认为您需要重新参加算法课程。在几乎排序的数据上(这是自适应情况),它是O(N)。但是,对数据进行2次传递,对于几乎排序的数据,插入仅花费1次,这使插入成为赢家。泡沫仍然不错
mmcdole

3
如果您的数据几乎没有排序,那么性能将严重下降。我个人不会使用它。
Blorgbeard将于

5
我尝试时该链接已断开。请尝试以下方法:sorting-algorithms.com
Michael La Voie

110

仅几项=>插入排序

项目大部分已经排序=>插入排序

担心最坏的情况=> HEAP SORT

对良好的平均情况结果感兴趣=> QUICKSORT

物品来自密集的宇宙=>桶装

希望编写尽可能少的代码=>插入排序


1
那正是我一直在寻找的答案,我读过书,但在特定情况下我似乎找不到任何关于选择alogorithms的明确解释,请您详细说明一下或通过链接,以便我可以追寻还有一点吗?谢谢
Simran kaur 2014年

9
您应该添加“数据已经按照其他条件排序=>合并排序”
Jim Hunziker

30

timsort

Timsort是“一种自适应,稳定,自然的合并排序”,具有“在许多部分有序数组上的超自然性能(少于lg(N!)个需要的比较,并且只有N-1个)”。Python的内置sort()已经使用此算法一段时间,显然效果良好。它是专门为检测和利用输入中部分排序的子序列而专门设计的,这些子序列通常出现在实际数据集中。在现实世界中,通常情况下,比较要比交换列表中的项目昂贵得多,因为比较通常仅交换指针,这经常使timsort成为绝佳选择。但是,如果您知道自己的比较总是很便宜的(例如,编写一个玩具程序来对32位整数进行排序),则可能存在其他性能更好的算法。利用timsort的最简单方法当然是使用Python,但是由于Python是开源的,因此您也可以借用代码。另外,上面的描述包含了足够多的细节来编写您自己的实现。


17
log(n!)为Ο(n * log(n)),因此它不是“超自然的”。
jfs



9
@JF Sebastian:timsort比lg(n!)几乎排序的数组上的比较要快得多,一直到O(n)!| @behrooz:没有任何一种比较排序的平均情况可以优于O(n log n),并且lg(n!)O(n log n)。因此,timsort的最坏情况在渐近性上不比任何其他比较类型差。此外,它的最佳情况优于或等于任何其他比较类别。
Artelius

3
在最坏的情况下,Timsort仍为O(nlogn),但其良好情况非常令人满意。这里有一个比较,有一些图表:stromberg.dnsalias.org/~strombrg/sort-comparison 注意timsort在用Cython是不是几乎一样快Python的内置timsort在C
user1277476

19

插入排序具有以下行为:

  1. 对于k插槽中的每个元素1..n,首先检查是否el[k] >= el[k-1]。如果是这样,请转到下一个元素。(显然跳过第一个元素。)
  2. 如果不是,请使用对元素的二进制搜索1..k-1来确定插入位置,然后将元素遍历。(只有在k>T其中T有某个阈值的情况下,您才可以执行此操作;如果设置的阈值太小,则可能会k导致过度杀伤力。)

此方法进行的比较最少。


我认为,如果未排序元素的数量非常少(例如一两个),那么冒泡排序可能会胜过这一点,但总的来说,这可能是我的最佳解决方案。
溶胶

由于执行了第1步,对于已排序的所有元素,仅存在一个比较而数据移动为零,这显然是您可以做的最好的。第2步是您可以改进的步骤,但是bubble将移动相同数量的元素,并且可能会有更多比较,具体取决于您的展示次数。
杰森·科恩

实际上,根据进一步的思考,我认为气泡排序比我以前想的要强。这实际上是一个相当棘手的问题。例如,如果您将列表完全排序(最后一个元素应排在第一位),则冒泡排序将大大优于您所描述的排序。
溶胶

我尝试实现此功能,但是二进制搜索没有太大的改进,因为您仍然必须移动整个块以插入元素。因此,您将获得range + logb(range)而不是2xrange。
这个

11

尝试自省排序。http://en.wikipedia.org/wiki/Introsort

它是基于快速排序的,但是避免了快速排序对几乎排序的列表具有的最坏情况的行为。

诀窍在于,这种排序算法可以检测快速排序进入最坏情况模式并切换到堆或合并排序的情况。通过一些非朴素的分区方法可以检测到几乎排序的分区,而使用插入排序可以处理小的分区。

您将获得所有主要排序算法中的精华,从而获得更多代码和更多复杂性。而且您可以确保无论数据看起来如何,都绝不会遇到最坏的情况。

如果您是C ++程序员,请检查std :: sort算法。它可能已在内部使用自省排序。


7

Splaysort是基于splay树(一种自适应二叉树)的模糊排序方法。Splaysort不仅适用于部分排序的数据,还适用于部分反向排序的数据,或者实际上适用于具有任何预先存在的顺序的任何数据。一般情况下为O(nlogn),数据以某种方式(正向,反向,风琴等)排序时为O(n)。

与插入排序相比,它的巨大优势在于,即使根本不对数据进行排序,它也不会还原为O(n ^ 2)行为,因此您无需绝对确定在使用数据之前对数据进行了部分排序。

其缺点是所需的展开树结构的额外空间开销,以及构建和销毁展开树所需的时间。但是,取决于数据的大小和您期望的预排序量,对于速度的提高,开销可能是值得的。

关于splaysort论文发表在《软件-实践与经验》上。



5

Dijkstra的smoothsort对已排序的数据非常有用。这是一个堆排序变体,以O(n lg n)最坏情况和O(n)最佳情况运行。如果您对算法的工作原理感到好奇,我就对算法进行了分析

自然合并排序是另一个非常好的方法-这是一个自下而上的合并排序变体,其工作方式是将输入视为多个不同排序范围的串联,然后使用合并算法将它们连接在一起。您重复此过程,直到对所有输入范围进行了排序。如果数据已经排序,则运行时间为O(n),最坏情况为O(n lg n)。它非常优雅,尽管在实践中不如Timsort或smoothsort等其他自适应类型好。


与其他排序算法相比,smoothsort的运行时常量是多少?(即针对相同数据的运行时(平滑排序)/运行时(插入排序))
Arne Babenhauserheide

4

如果元素已经排序或元素很少,那么这将是插入排序的理想用例!


3

插入排序需要时间O(n +反转次数)。

反转是一对(i, j),使得i < j && a[i] > a[j]。即,无序对。

进行“几乎排序”的一种方法是求反演次数-一种方法可以将“几乎排序的数据”表示为反演次数很少的数据。如果知道反转的数量是线性的(例如,您刚刚将O(1)元素追加到排序列表中),则插入排序将花费O(n)时间。


2

就像其他所有人所说的那样,请小心天真的Quicksort,因为Quicksort可能对已排序或​​几乎已排序的数据具有O(N ^ 2)性能。不过,使用适当的算法来选择枢轴(随机或三位数中位数-请参见为Quicksort选择枢轴),Quicksort仍然可以正常工作。

通常,选择诸如插入排序之类的算法的困难在于确定数据何时充分混乱,以至于Quicksort确实会更快。


2

我不会假装在这里得到所有答案,因为我认为要获得实际答案可能需要对算法进行编码,并针对代表性数据样本对它们进行性能分析。但是我整夜都在思考这个问题,这是到目前为止我发生的事情,并且有人猜测什么地方最有效。

令N为总数,M为无序数。

冒泡排序必须使2 * M + 1通过所有N个项目。如果M非常小(0、1、2?),我认为这将很难克服。

如果M小(例如小于log N),则插入排序将具有出色的平均性能。但是,除非有我看不到的技巧,否则它在最坏情况下的性能将非常糟糕。(对吗?如果顺序中的最后一个项目排在最前面,那么就我所知,您必须插入每个项目,这会降低性能。)我猜想这里有一个更可靠的排序算法情况,但我不知道这是什么。

如果M大于(例如等于或大于log N),则内省排序几乎可以肯定是最好的。

例外:如果您实际上提前知道哪些元素未排序,那么最好的选择是将这些元素取出,使用自省排序对它们进行排序,然后将两个排序的列表合并为一个排序的列表。如果您可以快速找出哪些项目不正常,那么这也是一个很好的通用解决方案-但我一直无法找出一种简单的方法来解决此问题。

进一步的考虑(一夜之间):如果M + 1 <N / M,则可以扫描列表以查找连续排列的N / M的行,然后向任一方向扩展该行以找出超出范围的行订单商品。最多需要2N的比较。然后,您可以对未排序的项目进行排序,并对两个列表进行排序合并。我认为,总的比较应该少于4N + M log2(M)之类的值,它将超过任何非专业的排序例程。(甚至进一步考虑:这比我想的要棘手,但我仍然认为这是合理可行的。)

对问题的另一种解释是,可能有许多乱序项目,但它们非常接近应在列表中的位置。(想象一下,从一个已排序的列表开始,然后将所有其他项目与它后面的项目交换。)在这种情况下,我认为气泡排序的效果非常好-我认为通过的次数与最远的项目成正比是。插入排序的效果很差,因为每个故障订单项都会触发插入。我怀疑内省型或类似的方法也能很好地工作。




0

插入排序是对排序输入的最佳情况O(n)。它与大多数排序的输入(优于快速排序)非常接近。


0

考虑尝试堆。我相信这是O(n lg n)排序中最一致的。


一致性在这里并不重要。堆排序即使在排序后的数据上也将给出O(n lg n),并且实际上不是自适应的。可行的选项可以是:插入排序,Timsort和Bubblesort。
Max

0

气泡排序(或更安全的双向气泡排序)可能是大多数排序列表的理想选择,尽管我敢打赌经过调整的梳状排序(初始间隙大小要小得多)在列表不存在时会更快一些。完全一样的排序。梳子排序降级为气泡排序。


0

好吧,这取决于用例。如果您知道要更改的元素,就我而言,删除和插入将是最好的情况。


1
这项关于算法效率的“就我而言”的测试使我兴奋不已:)认真地说,但是,当编写“删除并插入”时,您的意思是插入排序(在前面的答案中已经提到过),或者您提供一种新的算法?如果是这样,请扩大您的答案。
yoniLavi 2015年

0

气泡排序绝对是赢家。雷达上的下一个将是插入排序。


4
发表您的答案并附有解释;

1
我建议您在发布之前先看看可用的答案,以免重复。
angainor 2012年

-1

远离QuickSort-对于预排序的数据,效率非常低。插入排序通过移动尽可能少的值来很好地处理几乎排序的数据。


-1 Quicksort的每个工业实现都有合理的枢轴选择
Stephan Eggermont,09年

1
是的,但没有任何枢轴选择是完美的,除非它变得昂贵。
user1277476 2012年

我见过的大多数工业快速排序都是在数组块中的元素少于10或20时才切换为插入排序。
ggorlen
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.