何时使用合并排序以及何时使用快速排序?

时间:2011-10-24 16:27:33

标签: c++ sorting

wikipedia article for merge sort

wikipedia article for quick sort

这两篇文章都具有出色的可视化效果。

两者都有n * log(n)的复杂性。

显然,数据的分布会影响排序的速度。我的猜测是,由于比较可以快速比较任何两个值,无论它们的扩散如何,数据值的范围都无关紧要。

更重要的是,应该考虑关于排序的横向分布(x方向)(去除幅度)。

如果测试数据具有某种级别的排序,那么需要考虑的一个好的测试用例......

6 个答案:

答案 0 :(得分:14)

它通常取决于所涉及的数据结构。快速排序是 通常最快,但不保证O(n * log(n));有 退化的情况,它变成O(n ^ 2)。堆排序是通常的 替代;它保证O(n * log(n)),无论初始顺序如何, 但它具有更高的常数因子。它通常在你使用时使用 需要一个坚硬的上限时间。一些更新的算法 使用快速排序,但尝试识别它何时开始退化, 然后切换到堆排序。数据时使用合并排序 结构不支持随机访问,因为它适用于纯粹的 顺序访问(转发迭代器,而不是随机访问) 迭代器)。例如,它在std::list<>::sort中使用。这也是 广泛用于外部排序,随机访问可以非常非常 与顺序访问相比昂贵。 (当排序文件时 不适合记忆,你可能会把它分成适合的块 内存,使用quicksort对它们进行排序,然后将每个文件写入文件 合并排序生成的文件。)

答案 1 :(得分:9)

在处理链接列表时,Mergesort更快。这是因为在合并列表时可以很容易地更改指针。它只需要通过列表一次(O(n))。

Quicksort的就地算法需要移动(交换)数据。虽然这对于内存数据集非常有效,但如果您的数据集不适合内存,则可能会更加昂贵。结果将是大量的I / O.

现在,发生了很多并行化。并行化Mergesort比Quicksort(就地)更简单。如果不使用就地算法,则quicksort的空间复杂度为O(n),这与mergesort相同。

因此,总而言之,quicksort可能对适合内存的数据集更有效。对于更大的东西,最好使用mergesort。

使用mergesort而不是quicksort的另一般时间是数据非常相似(即,不接近统一)。 Quicksort依赖于使用数据透视表。在所有值都相似的情况下,快速排序击中O(n ^ 2)的最坏情况。如果数据的值非常相似,则更可能选择不良的数据透视导致非常不平衡的分区,从而导致O(n ^ 2)运行时。最直接的例子是列表中的所有值是否相同。

答案 2 :(得分:6)

有一种真实的排序算法 - 名为Timsort - 它确实利用了在野外遇到的数据经常被部分排序的想法。

该算法源自合并排序和插入排序,用于CPython,Java 7和Android。

有关详细信息,请参阅Wikipedia article

答案 3 :(得分:5)

在这两者中,当您需要稳定排序时使用合并排序。您可以使用修改后的快速排序(例如introsort),因为它往往更快,并且使用更少的内存。

Hoare所描述的普通老式Quicksort对于使其成为Theta(n^2)的性能破坏特殊情况非常敏感,因此您通常需要修改版本。这就是数据分发的来源,因为合并排序没有坏的情况。一旦你开始修改quicksort,你可以继续进行各种不同的调整,而introsort是更有效的调整之一。它会在运行中检测它是否处于杀手状态,如果是这样,则会切换到heapsort。

事实上,Hoare最基本的Quicksort在已经排序的数据中失败最差,所以你的“好的测试用例”会有一定程度的排序会将它杀死到某种程度。然而,这个事实只是为了好奇,因为它只需要一个非常小的调整就可以避免这种情况,没有什么比一直到内省更复杂。因此,甚至懒得分析被排序数据杀死的版本也很简单。

在实践中,在C ++中,您通常使用std::stable_sortstd::sort,而不是过多地担心确切的算法。

答案 4 :(得分:2)

虽然Java 6及更早版本使用合并排序作为排序算法,但C#使用QuickSort作为排序算法。

QuickSort的性能优于合并排序,即使它们都是O(nlogn)。 QuickSort的常量小于合并排序。

答案 5 :(得分:1)

请记住,在实践中,除非您拥有非常大的数据集和/或多次执行排序,否则根本不重要。话虽这么说,quicksort通常被认为是'最快'的n * log(n)分拣机。看到这个问题:Quick Sort Vs Merge Sort