为什么当 Timsort(根据维基百科)似乎表现得更好时,我大多听说 Quicksort 是最快的整体排序算法?谷歌似乎没有进行任何比较。
5 回答
TimSort 是一个高度优化的归并排序,它比旧归并排序更稳定、更快。
与快速排序相比,它有两个优点:
- 对于几乎排序的数据序列(包括反向排序的数据),它的速度快得令人难以置信;
- 最坏的情况仍然是 O(N*LOG(N))。
老实说,我不认为#1 是一个优势,但它确实给我留下了深刻的印象。
以下是 QuickSort 的优势
- QuickSort 非常非常简单,即使是高度优化的实现,我们可以在 20 行内写下它的伪代码;
- 在大多数情况下,快速排序是最快的;
- 内存消耗为 LOG(N)。
目前,Java 7 SDK 实现了 timsort 和一个新的快速排序变体:即 Dual Pivot QuickSort。
如果您需要稳定排序,请尝试 timsort,否则从 quicksort 开始。
一般来说,快速排序是原始数组的最佳算法。这是由于内存局部性和缓存。
JDK7 对 Object 数组使用 TimSort。对象数组只保存对象引用。对象本身存储在堆中。要比较对象,我们需要从堆中读取对象。这就像从堆的一部分读取一个对象,然后从堆的另一部分随机读取对象。会有很多缓存未命中。我想出于这个原因,内存局部性不再重要。这可能是 JDK 只对 Object 数组使用 TimSort 而不是原始数组的原因。
这只是我的猜测。
以下是我机器上的基准测试数据(i7-6700 CPU,3.4GHz,Ubuntu 16.04,gcc 5.4.0,参数:SIZE=100000 和 RUNS=3):
$ ./demo
Running tests
stdlib qsort time: 12246.33 us per iteration
##quick sort time: 5822.00 us per iteration
merge sort time: 8244.33 us per iteration
...
##tim sort time: 7695.33 us per iteration
in-place merge sort time: 6788.00 us per iteration
sqrt sort time: 7289.33 us per iteration
...
grail sort dyn buffer sort time: 7856.67 us per iteration
基准来自 Swenson 的排序项目,他在其中用 C 语言实现了几种排序算法。据推测,他的实现足以具有代表性,但我还没有研究过它们。
所以你真的说不出来。基准数字最多只能保持两年的相关性,然后您必须重复它们。2011 年,当有人问到这个问题时,timsort 可能击败了 qsort waaay,但时代已经改变。或者 qsort 总是最快的,但 timsort 在非随机数据上击败了它。或者 Swenson 的代码不是那么好,一个更好的程序员会扭转局面,有利于 timsort。CFLAGS
或者也许我在编译代码时很烂并且没有使用正确的。或者……你明白了。
如果您需要保持顺序的排序,或者如果您正在对复杂数组(比较基于堆的对象)而不是原始数组进行排序,则 Tim Sort 非常有用。正如其他人所提到的,快速排序从数据的局部性和原始数组的处理器缓存中受益匪浅。
提出了快速排序的最坏情况是 O(n^2) 的事实。幸运的是,您可以使用快速排序实现 O(n log n) 时间的最坏情况。快速排序最坏情况发生在枢轴点是最小值或最大值时,例如当枢轴是已排序数组的第一个或最后一个元素时。
我们可以通过将枢轴设置为中值来实现 O(n log n) 最坏情况快速排序。由于可以在线性时间 O(n) 内找到中值。由于 O(n) + O(n log n) = O(n log n),这成为最坏情况的时间复杂度。
然而,在实践中,大多数实现发现随机枢轴就足够了,因此不要搜索中值。