【发布时间】:2008-10-20 21:38:24
【问题描述】:
哪种排序算法对大多数排序的数据最有效?
【问题讨论】:
-
从缺乏上下文中猜测 - 您是在询问内存中的排序,不需要将中间结果溢出到磁盘?
-
According to these animations 插入排序最适合大多数已排序的数据。
哪种排序算法对大多数排序的数据最有效?
【问题讨论】:
基于观看animated gifs 的高度科学的方法,我会说插入和冒泡排序是很好的候选者。
【讨论】:
只有几项 => 插入排序
项目大多已经排序 => 插入排序
关注最坏情况 => 堆排序
对良好的平均情况结果感兴趣 => QUICKSORT
物品来自一个密集的宇宙 => BUCKET SORT
希望编写尽可能少的代码 => 插入排序
【讨论】:
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 的最坏情况在渐近上并不比任何其他比较排序差。此外,它的最佳情况优于或等于任何其他比较排序。
插入排序具有以下行为:
1..n中的每个元素k,首先检查是否el[k] >= el[k-1]。如果是这样,请转到下一个元素。 (显然跳过第一个元素。)1..k-1 中使用二分搜索来确定插入位置,然后搜索元素。 (只有当k>T 是某个阈值时,您才可以这样做;如果k 很小,这将是矫枉过正。)这种方法的比较次数最少。
【讨论】:
尝试内省排序。 http://en.wikipedia.org/wiki/Introsort
它是基于快速排序的,但它避免了快速排序对几乎排序列表的最坏情况。
诀窍在于,这种排序算法会检测到快速排序进入最坏情况模式并切换到堆排序或合并排序的情况。接近排序的分区通过一些非天真的分区方法检测,小分区使用插入排序处理。
以更多代码和复杂性为代价,您可以获得所有主要排序算法中最好的。而且您可以确保无论您的数据看起来如何,您都不会遇到最坏的情况.
如果您是 C++ 程序员,请检查您的 std::sort 算法。它可能已经在内部使用了自省排序。
【讨论】:
Splaysort是一种基于splay trees的模糊排序方法,splay trees是一种自适应二叉树。 Splaysort 不仅适用于部分排序的数据,也适用于部分反向排序的数据,或者实际上任何具有任何类型预先存在顺序的数据。在一般情况下是 O(nlogn),在数据以某种方式(正向、反向、风琴管等)排序的情况下是 O(n)。
与插入排序相比,它的最大优势在于,当数据根本没有排序时,它不会恢复为 O(n^2) 行为,因此您无需绝对确定数据是否部分排序使用前。
它的缺点是它需要展开树结构的额外空间开销,以及构建和销毁展开树所需的时间。但是根据您期望的数据大小和预排序数量,开销对于提高速度可能是值得的。
A paper on splaysort 发表于 Software--Practice & Experience。
【讨论】:
插入或shell排序!
【讨论】:
Dijkstra 的平滑排序是对已排序数据的出色排序。这是一个堆排序变体,在 O(n lg n) 最坏情况和 O(n) 最佳情况下运行。我wrote an analysis 的算法,以防你好奇它是如何工作的。
自然合并排序是另一个非常好的方法 - 它是一种自下而上的合并排序变体,通过将输入视为多个不同排序范围的串联,然后使用合并算法将它们连接在一起。您重复此过程,直到对所有输入范围进行排序。如果数据已经排序并且 O(n lg n) 最坏情况,这将在 O(n) 时间内运行。它非常优雅,尽管在实践中它不如 Timsort 或 Smoothsort 等其他自适应排序。
【讨论】:
如果元素已经排序或者只有很少的元素, 这将是插入排序的完美用例!
【讨论】:
插入排序需要时间 O(n + 反转次数)。
反转是一对(i, j),使得i < j && a[i] > a[j]。也就是乱序对。
“几乎排序”的一个衡量标准是反转的数量——可以将“几乎排序的数据”表示反转很少的数据。如果知道反转次数是线性的(例如,您刚刚将 O(1) 元素附加到排序列表中),则插入排序需要 O(n) 时间。
【讨论】:
正如其他人所说,小心天真的快速排序——它在排序或接近排序的数据上可能有 O(N^2) 的性能。尽管如此,使用适当的算法选择枢轴(随机或三的中位数 - 请参阅Choosing a Pivot for Quicksort),快速排序仍然可以正常工作。
一般来说,选择诸如插入排序之类的算法的困难在于确定数据何时足够乱序,以至于快速排序确实会更快。
【讨论】:
我不会假装在这里拥有所有答案,因为我认为获得实际答案可能需要对算法进行编码并根据代表性数据样本对其进行分析。但是我整个晚上都在思考这个问题,以下是我到目前为止发生的事情,以及一些关于什么地方最有效的猜测。
令 N 为总项目数,M 为无序数。
冒泡排序必须使类似 2*M+1 的东西通过所有 N 个项目。如果 M 非常小(0、1、2?),我认为这将很难被击败。
如果 M 很小(比如小于 log N),插入排序将具有很好的平均性能。但是,除非有我没有看到的技巧,否则它的最坏情况下的性能会很差。 (对吧?如果订单中的最后一项排在第一位,那么您必须插入每一项,据我所知,这会影响性能。)我猜有一个更可靠的排序算法可以解决这个问题案例,但我不知道它是什么。
如果 M 更大(比如等于或大于 log N),内省排序几乎肯定是最好的。
所有这些的例外:如果您确实提前知道哪些元素未排序,那么您最好的选择是将这些项目拉出,使用内省排序对它们进行排序,然后将两个排序列表合并为一个排序列表.如果您可以快速找出哪些项目有问题,这也是一个很好的通用解决方案——但我还没有找到一个简单的方法来做到这一点。
进一步的想法(一夜之间):如果 M+1
对这个问题的另一种解释是,可能有很多乱序的项目,但它们非常接近它们应该在列表中的位置。 (想象一下从一个排序列表开始,然后将所有其他项目与它之后的项目交换。)在这种情况下,我认为冒泡排序表现得非常好——我认为通过的次数将与最不合适的项目成正比是。插入排序效果不佳,因为每个乱序项都会触发插入。我怀疑内省排序或类似的方法也能很好地工作。
【讨论】:
如果您需要排序算法、数据结构或任何与上述内容相关的特定实现,我可以向您推荐 CodePlex 上出色的 "Data Structures and Algorithms" 项目吗?
它将拥有您需要的一切,而无需重新发明轮子。
只是我的一点点盐。
【讨论】:
答案中用于此目的的排序算法很好的集合,似乎缺少Gnome Sort,这也是合适的,并且可能需要最少的实现工作。
【讨论】:
插入排序是排序输入的最佳情况 O(n)。它非常接近大多数排序的输入(比快速排序更好)。
【讨论】:
冒泡排序(或者更安全的双向冒泡排序)可能是大多数排序列表的理想选择,尽管我敢打赌,经过调整的梳状排序(初始间隙大小要小得多)在列表没有那么完美地排序。梳排序降级为冒泡排序。
【讨论】:
这取决于用例。如果您知道更改了哪些元素,就我而言,删除和插入将是最好的情况。
【讨论】:
冒泡排序绝对是赢家 雷达上的下一个将是插入排序。
【讨论】:
思考试试堆。我相信它是 O(n lg n) 排序中最一致的。
【讨论】:
远离 QuickSort - 它对预先排序的数据非常低效。插入排序通过移动尽可能少的值来很好地处理几乎排序的数据。
【讨论】: