quicksort和heapsort都进行就地排序。哪个更好?什么是首选的应用程序和案例?
答案 0 :(得分:82)
Heapsort是O(N log N)保证,比Quicksort的最坏情况要好得多。根据Mergesort的需要,Heapsort不需要为另一个阵列提供更多内存来放置有序数据。那么为什么商业应用程序坚持使用Quicksort? Quicksort有什么特别优于其他实现?
我自己测试过这些算法,而且我已经看到Quicksort确实有一些特别之处。它运行速度快,比Heap和Merge算法快得多。
Quicksort的秘诀在于:它几乎不会进行不必要的元素交换。交换非常耗时。
使用Heapsort,即使您的所有数据都已经订购,您也要交换100%的元素来订购数组。
使用Mergesort,情况会更糟。您将在另一个数组中编写100%的元素,并将其写回原始数组中,即使已经订购了数据。
使用Quicksort你不会交换已经订购的东西。如果您的数据是完全订购的,那么几乎没有任何交换!虽然关于最坏情况有很多烦恼,但是对于枢轴选择的一点改进,除了获得数组的第一个或最后一个元素之外,可以避免它。如果从第一个,最后一个和中间元素之间的中间元素获得一个枢轴,那么避免最坏的情况就足够了。
Quicksort的优势不是最坏的情况,但是最好的情况!在最好的情况下,你做相同数量的比较,好吧,但你几乎没有交换。在一般情况下,您交换部分元素,但不是所有元素,如Heapsort和Mergesort。这就是Quicksort最好的时间。减少交换,提高速度。
我的计算机上的C#下面的实现,在发布模式下运行,使用中间数据枢轴击败Array.Sort 3秒,使用改进的数据透视表击败2秒(是的,有一个开销可以获得良好的支点)。
static void Main(string[] args)
{
int[] arrToSort = new int[100000000];
var r = new Random();
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
Console.WriteLine("Press q to quick sort, s to Array.Sort");
while (true)
{
var k = Console.ReadKey(true);
if (k.KeyChar == 'q')
{
// quick sort
Console.WriteLine("Beg quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
QuickSort(arrToSort, 0, arrToSort.Length - 1);
Console.WriteLine("End quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
else if (k.KeyChar == 's')
{
Console.WriteLine("Beg Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
Array.Sort(arrToSort);
Console.WriteLine("End Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
}
}
}
static public void QuickSort(int[] arr, int left, int right)
{
int begin = left
, end = right
, pivot
// get middle element pivot
//= arr[(left + right) / 2]
;
//improved pivot
int middle = (left + right) / 2;
int
LM = arr[left].CompareTo(arr[middle])
, MR = arr[middle].CompareTo(arr[right])
, LR = arr[left].CompareTo(arr[right])
;
if (-1 * LM == LR)
pivot = arr[left];
else
if (MR == -1 * LR)
pivot = arr[right];
else
pivot = arr[middle];
do
{
while (arr[left] < pivot) left++;
while (arr[right] > pivot) right--;
if(left <= right)
{
int temp = arr[right];
arr[right] = arr[left];
arr[left] = temp;
left++;
right--;
}
} while (left <= right);
if (left < end) QuickSort(arr, left, end);
if (begin < right) QuickSort(arr, begin, right);
}
答案 1 :(得分:46)
This paper有一些分析。
另外,来自维基百科:
最直接的竞争对手 quicksort是heapsort。 Heapsort是 通常有点慢 快速排序,但最坏的情况下运行 时间总是Θ(nlogn)。 Quicksort是 通常更快,但仍有 最坏情况表现的可能性 除了inososort变种,其中 当一个坏的情况下切换到heapsort 被检测到。如果事先知道的话 那个heapsort会是 必要的,直接使用它 比等待内向更快 切换到它。
答案 2 :(得分:14)
对于大多数情况来说,快速与快速相关是无关紧要的......你根本不想让它偶尔变得缓慢。虽然您可以调整QuickSort以避免缓慢的情况,但您会失去基本QuickSort的优雅。所以,对于大多数事情,我实际上更喜欢HeapSort ......你可以用它完全简单的优雅来实现它,而且永远不会慢慢排序。
对于大多数情况下你想要最大速度的情况,QuickSort可能比HeapSort更受欢迎,但两者都不是正确的答案。对于速度危急的情况,值得仔细研究情况的细节。例如,在我的一些速度关键代码中,数据已经排序或接近排序是很常见的(它正在索引多个相关字段,这些字段通常一起上下移动或上下移动,所以一旦你按一个排序,其他的排序或反向排序或关闭...其中任何一个都可以杀死QuickSort)。对于那种情况,我没有实现......相反,我实现了Dijkstra的SmoothSort ...一个HeapSort变体,当已经排序或接近排序时是O(N)......它不是那么优雅,不太容易理解,但是快...如果你想要一些更具挑战性的代码,请阅读http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF。
答案 3 :(得分:5)
Quicksort-Heapsort就地混合动力车也非常有趣,因为在最坏的情况下它们大多只需要n * log n比较(它们在渐近线的第一项中是最优的,所以它们避免了最坏的情况-Quicksort场景),O(log n)额外空间,它们至少保留了Quicksort关于已经排序的数据集的良好行为的“一半”。 Dikert和Weiss在http://arxiv.org/pdf/1209.4214v1.pdf中提出了一个非常有趣的算法:
答案 4 :(得分:2)
小样。在quick sort
和merge sort
之间,因为两者都是就地排序的类型,所以对于快速排序的wrost case运行时间的wrost case运行时间之间存在差异O(n^2)
并且对于堆排序它是仍然O(n*log(n))
,对于平均数据量,快速排序会更有用。由于它是随机算法所以得到正确的ans的概率。在更短的时间内将取决于您选择的枢轴元素的位置。
所以
好的电话: L和G的尺寸均小于3s / 4
通话不良: L和G中的一个大小超过3秒/ 4
对于少量我们可以进行插入排序,并且非常大量的数据用于堆排序。
答案 5 :(得分:2)
好吧,如果你去建筑层......我们在缓存内存中使用队列数据结构。那么队列中可用的东西将被排序。在快速排序中,我们没有问题将数组划分为任何长度......但是在堆排序中(通过使用数组)可能会发生这样的情况:父节点可能不存在于高速缓存中可用的子数组中,然后它必须将它带入高速缓冲存储器......这是非常耗时的。 快速排序是最好的!!
答案 6 :(得分:1)
Heapsort的好处是运行情况最糟糕的是 O(n * log(n)),因此在快速排序可能表现不佳的情况下(通常是大多数排序的数据集),heapsort是非常喜欢。
答案 7 :(得分:1)
Heapsort构建一个堆,然后重复提取最大项。最糟糕的情况是O(n log n)。
但如果您看到quick sort的最坏情况,即O(n2),您会意识到快速排序对于大数据来说是不太好的选择。
所以这使得排序是一件有趣的事情;我相信今天有这么多排序算法的原因是因为它们都是最好的地方“最好”。例如,如果对数据进行排序,冒泡排序可以执行快速排序。或者,如果我们对要排序的项目有所了解,那么我们可能会做得更好。
这可能不会直接回答你的问题,我想加上我的两分钱。
答案 8 :(得分:1)
在处理非常大的输入时,堆排序是一个安全的选择。渐近分析揭示了Heapsort在最坏情况下的增长顺序是Big-O(n logn)
,这比Quicksort的Big-O(n^2)
更好。但是,Heapsort在大多数机器上实际上比实现快速排序要慢一些。 Heapsort也不是一个稳定的排序算法。
heapsort在实践中比quicksort慢的原因是由于quicksort中的引用(“https://en.wikipedia.org/wiki/Locality_of_reference”)更好的位置,其中数据元素位于相对较近的存储位置。具有强参考局部性的系统是性能优化的理想选择。然而,堆排序处理更大的跳跃。这使得quicksort更适合较小的输入。
答案 9 :(得分:1)
对我来说,heapsort和quicksort之间有一个非常根本的区别:后者使用递归。在递归算法中,堆随着递归的数量而增长。如果 n 很小,这无关紧要,但现在我正在用 n = 10 ^ 9 !!对两个矩阵进行排序。该程序需要大约10 GB的内存,任何额外的内存将使我的计算机开始交换到虚拟磁盘内存。我的磁盘是一个RAM磁盘,但仍然交换它会产生巨大的速度差异。所以在用C ++编写的包含可调维度矩阵的statpack中,程序员的大小未知,非参数统计类型的排序我更喜欢heapsort以避免延迟使用非常大的数据矩阵。
答案 10 :(得分:0)
简单地说>> HeapSort保证了〜O(n log n)的最坏情况下的运行时间,而不是QuickSort的 平均运行时间为“ O(n log n)”。在实践中通常使用QuickSort,因为通常它速度更快,但是 当您需要对不适合您内存的大文件进行排序时,HeapSort用于外部排序 电脑。
答案 11 :(得分:-1)
回答原始问题,并在此处解决其他一些评论:
我只是比较了选择,快速,合并和堆排序的实现,以了解它们如何相互叠加。答案是他们都有自己的缺点。
TL; DR: Quick是最好的通用类型(合理快速,稳定,大部分就地) 我个人更喜欢堆排序,除非我需要一个稳定的排序。
选择 - N ^ 2 - 它真的只适用于少于20个元素左右,然后它的表现优于其他。除非您的数据已经排序,或者非常非常接近。 N ^ 2变得非常慢,非常快。
根据我的经验,很快, 使用快速排序作为一般排序的奖励是它的速度相当快且稳定。它也是就地算法,但由于它通常以递归方式实现,因此会占用额外的堆栈空间。它也介于O(n log n)和O(n ^ 2)之间。某些种类的时机似乎证实了这一点,特别是当价值落在一个狭窄的范围内时。它比10,000,000个项目的选择排序更快,但比合并或堆慢。
合并排序保证为O(n log n),因为它的排序不依赖于数据。无论你给出了什么价值,它只是做它做的事情。它也很稳定,但如果您不小心实施,那么非常大的种类可能会破坏您的筹码。有一些复杂的就地合并排序实现,但通常您需要在每个级别中另一个数组来合并您的值。如果这些阵列存在于堆栈中,则可能会遇到问题。
堆排序是最大O(n log n),但在许多情况下更快,具体取决于您必须将值移动到log n深堆中的距离。堆可以很容易地在原始数组中就地实现,因此它不需要额外的内存,并且它是迭代的,因此在递归时不必担心堆栈溢出。堆排序的巨大的缺点是它不是一个稳定的排序,这意味着如果你需要它就会出现。