为什么在实践中使用quicksort?

时间:2009-04-21 09:55:41

标签: performance algorithm sorting quicksort

Quicksort具有最差的O(n 2 )性能,但无论如何仍然在实践中广泛使用。这是为什么?

8 个答案:

答案 0 :(得分:25)

你不应该仅以最坏情况为中心,而且只能按时间复杂度。它更多的是关于平均而不是最差,而且是时间空间。

快速排序:

  • 的平均时间复杂度为Θ( n log n );
  • 可以用空间复杂度Θ(log n );
  • 来实现

还考虑到大O表示法没有考虑任何常量,但实际上如果算法快几倍,它确实会有所不同。 Θ( n log n )表示该算法在 K n 日志中执行( n ),其中 K 是常数。 Quicksort是具有最低 K 的比较排序算法。

答案 1 :(得分:7)

QuickSort的平均渐近阶数为O(nlogn),由于常数较小(环路较紧密),因此通常比heapsort更有效。实际上,有一个理论线性时间中值选择算法可用于始终找到最佳枢轴,从而导致最坏情况O(nlogn)。但是,正常的QuickSort通常比理论上快。

为了使其更合理,请考虑QuickSort在O(n 2 )中完成的概率。它只是1/n!,这意味着它几乎不会遇到那种不好的情况。

答案 2 :(得分:6)

有趣的是,quicksort平均比mergesort执行更多的比较 - 快速排序为1.44 n lg n(预期),而mergesort为n lg n。如果重要的是比较,那么mergesort将非常适合快速排序。

快速排序快速的原因在于它具有许多其他理想的属性,这些属性在现代硬件上非常有效。例如,quicksort不需要动态分配。它可以在原始数组上就地工作,仅使用O(log n)堆栈空间(如果正确实现,则最坏情况)来存储递归所需的堆栈帧。尽管可以使用mergesort来执行此操作,但这样做通常会在合并步骤中造成巨大的性能损失。其他排序算法(如heapsort)也具有此属性。

此外,quicksort具有出色的参考位置。如果使用Hoare的就地分区算法完成分区步骤,则基本上是从阵列两端向内执行的两个线性扫描。这意味着快速排序将具有非常少量的高速缓存未命中,这在现代体系结构中对性能至关重要。另一方面,Heapsort没有非常好的局部性(它遍布整个数组),尽管大多数mergesort实现具有合理的局部性。

Quicksort也非常可并行化。一旦发生初始分区步骤以将阵列分成更小和更大的区域,那么这两个部分可以彼此独立地分类。许多排序算法可以并行化,包括mergesort,但由于上述原因,并行快速排序的性能往往优于其他并行算法。另一方面,Heapsort没有。

快速排序的唯一问题是它可能会降级为O(n 2 ),这在大型数据集上会非常严重。避免这种情况的一种方法是让算法对自身进行内省,并在其退化的情况下切换到较慢但更可靠的算法之一。这种算法称为introsort,是一种很好的混合排序算法,它可以在没有病态的情况下获得快速排序的许多好处。

总结:

  • Quicksort就位,除了递归中使用的堆栈帧,它占用O(log n)空间。
  • Quicksort具有良好的参考位置。
  • Quicksort很容易并行化。

这解释了为什么quicksort倾向于优于在纸上可能更好的排序算法。

希望这有帮助!

答案 3 :(得分:1)

因为平均而言,它是最快的比较排序(就经过的时间而言)。

答案 4 :(得分:1)

因为,在一般情况下,它是最快的排序算法之一。

答案 5 :(得分:1)

除了速度最快之外,可以通过在对数组进行排序之前对数组进行混洗来避免一些不良情况。至于它与小数据集的弱点,显然不是一个大问题,因为数据集很小,排序时间可能很小。

作为一个例子,我为QuickSort和bubble排序编写了一个python函数。冒泡分类需要大约20秒来分类10,000条记录,11秒分类为7500条,5条分类为5000条。快速排序可以在大约0.15秒内完成所有这些排序!

答案 6 :(得分:0)

值得指出的是,C确实具有库函数qsort(),但并不要求使用实际的QuickSort实现,这取决于编译器供应商。

答案 7 :(得分:0)

Bcs这是一个算法,可以很好地处理具有O(NlogN)复杂性的大数据集。这也是采用恒定空间的算法。通过明智地选择枢轴元素,我们可以避免更糟糕的快速排序情况,并且即使在排序的数组上也会在O(NlogN)中执行。