【问题标题】:Which sort algorithm works best on mostly sorted data? [closed]哪种排序算法最适用于大多数排序的数据? [关闭]
【发布时间】:2008-10-20 21:38:24
【问题描述】:

哪种排序算法对大多数排序的数据最有效?

【问题讨论】:

  • 从缺乏上下文中猜测 - 您是在询问内存中的排序,不需要将中间结果溢出到磁盘?
  • According to these animations 插入排序最适合大多数已排序的数据。

标签: algorithm sorting


【解决方案1】:

基于观看animated gifs 的高度科学的方法,我会说插入和冒泡排序是很好的候选者。

【讨论】:

  • 顺便说一句,这是一个很好的链接,荣誉和 +1
  • 冒泡排序很糟糕。它总是 O(n^2)。请至少将其从您的答案中剔除,以确保它是正确的。
  • jjnguy,这完全是错误的。我认为您需要重新学习算法课程。在几乎排序的数据(这是自适应情况)上,它是 O(N)。但是,它需要 2 次遍历数据,而对于几乎排序的数据,插入只需要 1 次,这使得 Insertion 成为赢家。不过泡泡还是不错的
  • 如果您的数据几乎没有排序,性能会严重下降。我个人仍然不会使用它。
  • 那个链接在我尝试的时候被破坏了。试试这个:sorting-algorithms.com
【解决方案2】:

只有几项 => 插入排序

项目大多已​​经排序 => 插入排序

关注最坏情况 => 堆排序

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

物品来自一个密集的宇宙 => BUCKET SORT

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

【讨论】:

  • 这正是我一直在寻找的答案,我读过书,但我似乎没有找到任何关于在特定情况下选择算法的明确解释,请您详细说明或通过一个链接,以便我可以更深入地了解它?谢谢
  • 您应该添加“数据已按另一个标准排序 => MERGE SORT”
  • @JimHunziker 你能链接到哪里我可以找到利用你提到的模式的合并排序实现吗?还是只是任何普通的合并排序都没有任何改变?
  • @python_learner 因为合并排序是一种稳定的排序,在新排序中具有相同键的项目将保持按旧排序排序。例如,如果您有一个按名字排序的列表,然后您合并按姓氏排序,则姓氏为 Smith 的人仍将按他们的名字排序。这适用于归并排序的所有实现。
【解决方案3】:

时间排序

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

【讨论】:

  • log(n!) 是 Ο(n*log(n)) 因此它不是“超自然的”。
  • log(n!) 并不快。 wolframalpha.com/input/?i=plot[log(N!),{N,0,1000}]
  • @J.F. Sebastian:timsort 比lg(n!) 在几乎排序的数组上的比较快得多,一直到O(n)! | @behrooz:没有比较排序的平均情况比O(n log n) 更好,lg(n!)O(n log n)。所以 timsort 的最坏情况在渐近上并不比任何其他比较排序差。此外,它的最佳情况优于或等于任何其他比较排序。
  • Timsort 在最坏的情况下仍然是 O(nlogn),但它的好情况非常令人愉悦。这是一个比较,带有一些图表:stromberg.dnsalias.org/~strombrg/sort-comparison 请注意,Cython 中的 timsort 不如 Python 在 C 中内置的 timsort 快。
【解决方案4】:

插入排序具有以下行为:

  1. 对于插槽1..n中的每个元素k,首先检查是否el[k] >= el[k-1]。如果是这样,请转到下一个元素。 (显然跳过第一个元素。)
  2. 如果不是,请在元素1..k-1 中使用二分搜索来确定插入位置,然后搜索元素。 (只有当k>T 是某个阈值时,您才可以这样做;如果k 很小,这将是矫枉过正。)

这种方法的比较次数最少。

【讨论】:

  • 我认为如果未排序元素的数量非常少(例如,一两个),冒泡排序可能会击败它,但总的来说,这可能是最好的解决方案。
  • 由于第 1 步,对于任何已经排序的元素,只有一次比较和零数据移动,这显然是您能做的最好的事情。第 2 步是您可以改进的地方,但气泡会移动相同数量的元素,并且可能有更多比较,具体取决于您的 impl。
  • 实际上,经过进一步思考,我认为冒泡排序比我想象的要强大。这实际上是一个相当棘手的问题。例如,如果您采用列表完全排序但应该排在最后的元素是第一个的情况,冒泡排序将大大优于您所描述的。
  • 我试图实现这一点,但二进制搜索并没有太大的改进,因为您仍然需要移动整个块才能插入元素。所以你得到 range + logb(range) 而不是 2xrange。
【解决方案5】:

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

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

诀窍在于,这种排序算法会检测到快速排序进入最坏情况模式并切换到堆排序或合并排序的情况。接近排序的分区通过一些非天真的分区方法检测,小分区使用插入排序处理。

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

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

【讨论】:

    【解决方案6】:

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

    与插入排序相比,它的最大优势在于,当数据根本没有排序时,它不会恢复为 O(n^2) 行为,因此您无需绝对确定数据是否部分排序使用前。

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

    A paper on splaysort 发表于 Software--Practice & Experience。

    【讨论】:

      【解决方案7】:

      插入或shell排序!

      【讨论】:

        【解决方案8】:

        Dijkstra 的平滑排序是对已排序数据的出色排序。这是一个堆排序变体,在 O(n lg n) 最坏情况和 O(n) 最佳情况下运行。我wrote an analysis 的算法,以防你好奇它是如何工作的。

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

        【讨论】:

        • 与其他排序算法相比,smoothsort 的运行时常量是什么? (即相同数据的运行时(平滑排序)/运行时(插入排序))
        【解决方案9】:

        如果元素已经排序或者只有很少的元素, 这将是插入排序的完美用例!

        【讨论】:

          【解决方案10】:

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

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

          “几乎排序”的一个衡量标准是反转的数量——可以将“几乎排序的数据”表示反转很少的数据。如果知道反转次数是线性的(例如,您刚刚将 O(1) 元素附加到排序列表中),则插入排序需要 O(n) 时间。

          【讨论】:

            【解决方案11】:

            正如其他人所说,小心天真的快速排序——它在排序或接近排序的数据上可能有 O(N^2) 的性能。尽管如此,使用适当的算法选择枢轴(随机或三的中位数 - 请参阅Choosing a Pivot for Quicksort),快速排序仍然可以正常工作。

            一般来说,选择诸如插入排序之类的算法的困难在于确定数据何时足够乱序,以至于快速排序确实会更快。

            【讨论】:

              【解决方案12】:

              我不会假装在这里拥有所有答案,因为我认为获得实际答案可能需要对算法进行编码并根据代表性数据样本对其进行分析。但是我整个晚上都在思考这个问题,以下是我到目前为止发生的事情,以及一些关于什么地方最有效的猜测。

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

              冒泡排序必须使类似 2*M+1 的东西通过所有 N 个项目。如果 M 非常小(0、1、2?),我认为这将很难被击败。

              如果 M 很小(比如小于 log N),插入排序将具有很好的平均性能。但是,除非有我没有看到的技巧,否则它的最坏情况下的性能会很差。 (对吧?如果订单中的最后一项排在第一位,那么您必须插入每一项,据我所知,这会影响性能。)我猜有一个更可靠的排序算法可以解决这个问题案例,但我不知道它是什么。

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

              所有这些的例外:如果您确实提前知道哪些元素未排序,那么您最好的选择是将这些项目拉出,使用内省排序对它们进行排序,然后将两个排序列表合并为一个排序列表.如果您可以快速找出哪些项目有问题,这也是一个很好的通用解决方案——但我还没有找到一个简单的方法来做到这一点。

              进一步的想法(一夜之间):如果 M+1

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

              【讨论】:

                【解决方案13】:

                如果您需要排序算法、数据结构或任何与上述内容相关的特定实现,我可以向您推荐 CodePlex 上出色的 "Data Structures and Algorithms" 项目吗?

                它将拥有您需要的一切,而无需重新发明轮子。

                只是我的一点点盐。

                【讨论】:

                  【解决方案14】:

                  答案中用于此目的的排序算法很好的集合,似乎缺少Gnome Sort,这也是合适的,并且可能需要最少的实现工作。

                  【讨论】:

                    【解决方案15】:

                    插入排序是排序输入的最佳情况 O(n)。它非常接近大多数排序的输入(比快速排序更好)。

                    【讨论】:

                      【解决方案16】:

                      冒泡排序(或者更安全的双向冒泡排序)可能是大多数排序列表的理想选择,尽管我敢打赌,经过调整的梳状排序(初始间隙大小要小得多)在列表没有那么完美地排序。梳排序降级为冒泡排序。

                      【讨论】:

                        【解决方案17】:

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

                        【讨论】:

                        • 这个“就我而言”的算法效率测试让我的一天变得更加美好了 :) 严肃地说,当写“删除和插入”时,你的意思是插入排序吗(在以前的答案),还是您提供一种新的算法?如果是这样,请扩大您的答案。
                        【解决方案18】:

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

                        【讨论】:

                        • 发布您的答案并附上解释;
                        • 我建议您在发布之前查看可用的答案以避免重复。
                        【解决方案19】:

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

                        【讨论】:

                        • 这里不关心一致性。 Heapsort 即使在已排序的数据上也会给出 O(n lg n),并且不是真正的自适应。可行的选项可以是:插入排序、Timsort 和 Bubblesort。
                        【解决方案20】:

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

                        【讨论】:

                        • -1 Quicksort 的每个工业实现都有一个合理的支点选择
                        • 是的,但没有任何枢轴选择是完美的,除非它变得昂贵。
                        • 我见过的大多数工业快速排序只是在数组块中的元素少于 10 或 20 个时切换到插入排序。
                        猜你喜欢
                        • 1970-01-01
                        • 2020-02-16
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2012-03-18
                        • 2011-11-14
                        • 1970-01-01
                        相关资源
                        最近更新 更多