哪种排序算法最适合大多数排序的数据?
哪种排序算法最适合大多数排序的数据?
Answers:
基于观看动画gif的高度科学的方法,我会说插入和冒泡排序是不错的选择。
仅几项=>插入排序
项目大部分已经排序=>插入排序
担心最坏的情况=> HEAP SORT
对良好的平均情况结果感兴趣=> QUICKSORT
物品来自密集的宇宙=>桶装
希望编写尽可能少的代码=>插入排序
Timsort是“一种自适应,稳定,自然的合并排序”,具有“在许多部分有序数组上的超自然性能(少于lg(N!)个需要的比较,并且只有N-1个)”。Python的内置sort()
已经使用此算法一段时间,显然效果良好。它是专门为检测和利用输入中部分排序的子序列而专门设计的,这些子序列通常出现在实际数据集中。在现实世界中,通常情况下,比较要比交换列表中的项目昂贵得多,因为比较通常仅交换指针,这经常使timsort成为绝佳选择。但是,如果您知道自己的比较总是很便宜的(例如,编写一个玩具程序来对32位整数进行排序),则可能存在其他性能更好的算法。利用timsort的最简单方法当然是使用Python,但是由于Python是开源的,因此您也可以借用代码。另外,上面的描述包含了足够多的细节来编写您自己的实现。
lg(n!)
几乎排序的数组上的比较要快得多,一直到O(n)
!| @behrooz:没有任何一种比较排序的平均情况可以优于O(n log n)
,并且lg(n!)
为O(n log n)
。因此,timsort的最坏情况在渐近性上不比任何其他比较类型差。此外,它的最佳情况优于或等于任何其他比较类别。
插入排序具有以下行为:
k
插槽中的每个元素1..n
,首先检查是否el[k] >= el[k-1]
。如果是这样,请转到下一个元素。(显然跳过第一个元素。)1..k-1
来确定插入位置,然后将元素遍历。(只有在k>T
其中T
有某个阈值的情况下,您才可以执行此操作;如果设置的阈值太小,则可能会k
导致过度杀伤力。)此方法进行的比较最少。
尝试自省排序。http://en.wikipedia.org/wiki/Introsort
它是基于快速排序的,但是避免了快速排序对几乎排序的列表具有的最坏情况的行为。
诀窍在于,这种排序算法可以检测快速排序进入最坏情况模式并切换到堆或合并排序的情况。通过一些非朴素的分区方法可以检测到几乎排序的分区,而使用插入排序可以处理小的分区。
您将获得所有主要排序算法中的精华,从而获得更多代码和更多复杂性。而且您可以确保无论数据看起来如何,都绝不会遇到最坏的情况。
如果您是C ++程序员,请检查std :: sort算法。它可能已在内部使用自省排序。
Splaysort是基于splay树(一种自适应二叉树)的模糊排序方法。Splaysort不仅适用于部分排序的数据,还适用于部分反向排序的数据,或者实际上适用于具有任何预先存在的顺序的任何数据。一般情况下为O(nlogn),数据以某种方式(正向,反向,风琴等)排序时为O(n)。
与插入排序相比,它的巨大优势在于,即使根本不对数据进行排序,它也不会还原为O(n ^ 2)行为,因此您无需绝对确定在使用数据之前对数据进行了部分排序。
其缺点是所需的展开树结构的额外空间开销,以及构建和销毁展开树所需的时间。但是,取决于数据的大小和您期望的预排序量,对于速度的提高,开销可能是值得的。
关于splaysort的论文发表在《软件-实践与经验》上。
Dijkstra的smoothsort对已排序的数据非常有用。这是一个堆排序变体,以O(n lg n)最坏情况和O(n)最佳情况运行。如果您对算法的工作原理感到好奇,我就对算法进行了分析。
自然合并排序是另一个非常好的方法-这是一个自下而上的合并排序变体,其工作方式是将输入视为多个不同排序范围的串联,然后使用合并算法将它们连接在一起。您重复此过程,直到对所有输入范围进行了排序。如果数据已经排序,则运行时间为O(n),最坏情况为O(n lg n)。它非常优雅,尽管在实践中不如Timsort或smoothsort等其他自适应类型好。
插入排序需要时间O(n +反转次数)。
反转是一对(i, j)
,使得i < j && a[i] > a[j]
。即,无序对。
进行“几乎排序”的一种方法是求反演次数-一种方法可以将“几乎排序的数据”表示为反演次数很少的数据。如果知道反转的数量是线性的(例如,您刚刚将O(1)元素追加到排序列表中),则插入排序将花费O(n)时间。
就像其他所有人所说的那样,请小心天真的Quicksort,因为Quicksort可能对已排序或几乎已排序的数据具有O(N ^ 2)性能。不过,使用适当的算法来选择枢轴(随机或三位数中位数-请参见为Quicksort选择枢轴),Quicksort仍然可以正常工作。
通常,选择诸如插入排序之类的算法的困难在于确定数据何时充分混乱,以至于Quicksort确实会更快。
我不会假装在这里得到所有答案,因为我认为要获得实际答案可能需要对算法进行编码,并针对代表性数据样本对它们进行性能分析。但是我整夜都在思考这个问题,这是到目前为止我发生的事情,并且有人猜测什么地方最有效。
令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)之类的值,它将超过任何非专业的排序例程。(甚至进一步考虑:这比我想的要棘手,但我仍然认为这是合理可行的。)
对问题的另一种解释是,可能有许多乱序项目,但它们非常接近应在列表中的位置。(想象一下,从一个已排序的列表开始,然后将所有其他项目与它后面的项目交换。)在这种情况下,我认为气泡排序的效果非常好-我认为通过的次数与最远的项目成正比是。插入排序的效果很差,因为每个故障订单项都会触发插入。我怀疑内省型或类似的方法也能很好地工作。
答案中的这个很好的排序算法集合似乎缺少Gnome Sort,它也很合适,并且可能需要最少的实现工作。
考虑尝试堆。我相信这是O(n lg n)排序中最一致的。
好吧,这取决于用例。如果您知道要更改的元素,就我而言,删除和插入将是最好的情况。
远离QuickSort-对于预排序的数据,效率非常低。插入排序通过移动尽可能少的值来很好地处理几乎排序的数据。