【问题标题】:When merge sort is preferred over Quick sort?何时合并排序优于快速排序?
【发布时间】:2015-05-26 22:14:12
【问题描述】:

在很多情况下,快速排序比归并排序要好得多。不过,在什么情况下合并排序可能比快速排序更好?

例如,当数据不能一次加载到内存时,合并排序比快速排序更有效。还有其他情况吗?

编辑: 建议的重复问题的答案列出了快速排序相对于合并排序的所有优点。我在这里询问使用合并排序比使用快速排序更有利的可能案例和应用程序。

【问题讨论】:

标签: algorithm sorting quicksort mergesort


【解决方案1】:

我可能应该首先提到,如果您不能一次将所有内容都放入内存,那么快速排序和合并排序都可以正常工作。您可以通过选择一个枢轴来实现快速排序,然后将元素从磁盘流式传输到内存中,并根据该元素与枢轴的比较情况将元素写入两个不同文件之一。如果您使用双端优先级队列,您实际上可以通过一次将最大数量的可能元素放入内存来更有效地做到这一点。

其他人提到归并排序是最坏情况 O(n log n) 的好处,这绝对是正确的。也就是说,您可以轻松地修改快速排序以生成 introsort 算法,这是快速排序、插入排序和堆排序之间的混合体,这是最坏情况的 O(n log n),但在大多数情况下仍保持快速排序的速度。

了解为什么快速排序通常比合并排序更快可能会有所帮助,因为如果您了解原因,您可以很快找到合并排序明显胜出的某些情况。快速排序通常比归并排序更好,原因有两个:

  1. 快速排序比归并排序具有更好的引用局部性,这意味着在快速排序中执行的访问通常比归并排序中的相应访问更快。

  2. 快速排序使用最坏情况的 O(log n) 内存(如果实现正确),而由于合并的开销,合并排序需要 O(n) 内存。

不过,在一种情况下,这些优势会消失。假设您要对元素的链表进行排序。链表元素分散在整个内存中,因此优势(1)消失了(没有引用的局部性)。其次,链表可以仅用 O(1) 空间开销而不是 O(n) 空间开销进行合并,因此优势 (2) 消失了。因此,您通常会发现归并排序是一种用于对链表进行排序的出色算法,因为它进行的总比较次数更少,并且不易受到糟糕的枢轴选择的影响。

希望这会有所帮助!

【讨论】:

  • 此外,mergesort 通常是一种就地排序,在按列标题排序时很有用。
  • @xpda 错了!归并排序最常见的实现具有空间复杂度 O(n),因此它不是就地的。有些实现是就地的,但它们要么不像原来的那样稳定,要么会增加性能的复杂性。参考:en.wikipedia.org/wiki/Merge_sort
  • @AlanEvangelista 我在后半部分讨论的情况,其中元素是链表,实际上并不需要线性辅助内存。我们可以简单地改变列表中元素之间的链接以形成必要的子列表,而不是将这些元素复制到临时数组中。检查维基百科页面上的信息框进行确认。
  • @templatetypedef 是的,我知道在对链表进行排序时,mergesort 的空间复杂度是 O(1),我之前的评论专门提到了 xpda 的错误语句,该语句也适用于普通的 mergesort。跨度>
  • @roulette01 有一个标准优化可以在快速排序上执行,它本质上是一个尾调用消除。与其进行两次递归调用,不如在两个子数组中较小的一个上触发递归调用,然后将当前堆栈帧中的空间重新用于较大的子数组。由于在每次新的递归调用中处理的子数组的大小最多为前一个的一半,因此使用的总空间为 O(log n)。
【解决方案2】:
  1. MergeSort 在设计上是稳定的,相等的元素保持原来的顺序。
  2. MergeSort 非常适合并行(多线程)实现。
  3. MergeSort 使用的比较比 QuickSort 少(大约 30%)。这是一个经常被忽视的优势,因为比较可能会非常昂贵(例如,比较数据库行的多个字段时)。

【讨论】:

  • 你能提供 2 和 3 的来源吗?另外,快速排序不是也适合多线程吗?
  • @blumonkey - 我自己编写了源代码,它是 C# 中的 parallel mergesort 实现。很少有问题,可以像这个算法更好地划分为独立的子任务。关于比较,Wikipedia 的信息相同,对应我自己的测试。
  • 2 的另一个来源是 Thomas H. Cormen 等人的《算法介绍》一书,第三版。有一个完整的部分解释了如何实现合并排序的多线程版本。该部分是 27.3 多线程合并排序,第 797 页。
【解决方案3】:

与快速排序相比,归并排序的一个最重要的优势是它的稳定性:比较相等的元素保留其原始顺序。

【讨论】:

    【解决方案4】:

    快速排序是平均情况 O(n log n),但最坏情况是 O(n^2)。合并排序总是 O(n log n)。除了渐近最坏情况和归并排序的内存加载,我想不出其他原因。

    快速排序比归并排序更差的场景:

    1. 数组已排序。
    2. 数组中的所有元素都相同。
    3. 数组以相反的顺序排序。

    如果您对数据一无所知,请使用合并排序而不是快速排序。

    【讨论】:

    • 对于场景 #1 和 #3,这取决于您选择轴心的方式。几乎每个常见的实现都使用最佳的三个来专门避免这两个。最坏的情况仍然是 O(n^2),但没有简单的模式可以达到这种情况。相同数量的模式,它们并不简单。
    【解决方案5】:

    归并排序的上限保证为 O(N log2N)。快速排序也有这样的限制,但要高得多 - 它是 O(N2)。当您需要保证代码时序的上限时,请使用合并排序而不是快速排序。

    例如,如果您为依赖排序的实时系统编写代码,则归并排序将是更好的选择。

    【讨论】:

      【解决方案6】:
      1. 合并排序最坏情况复杂度为 O(nlogn),而快速排序最坏情况复杂度为 O(n^2)。
      2. 归并排序是一种稳定的排序方式,这意味着数组中的相同元素彼此保持其原始位置。

      【讨论】:

      • 这在其他答案中已经回答了好几次了。
      猜你喜欢
      • 1970-01-01
      • 2011-07-10
      • 2015-06-28
      • 2021-06-06
      • 2010-10-04
      • 1970-01-01
      • 2021-01-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多