Quicksort 的一个众所周知的问题是,当数据集处于或几乎处于排序顺序时,性能会严重下降。在这种情况下,通常非常慢的插入排序很容易成为最佳选择。问题是知道何时使用哪个。
是否有一种算法可用于遍历数据集、应用比较因子并返回关于数据集与排序顺序的接近程度的报告?我更喜欢 Delphi/Pascal,但如果示例不太复杂,我可以阅读其他语言。
还有 SmoothSort,这显然很难实现,但它在 O(N log N) 到 O(N) 之间变化,具体取决于数据开始的排序方式。
http://en.wikipedia.org/wiki/Smoothsort
长而棘手的 PDF: http ://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF
但是,如果您的数据确实很大并且您必须连续访问它,那么合并排序可能是最好的。它总是 O(N log N) 并且具有出色的“局部性”特性。
我没有听说过任何预排序分析,但我的观点是,如果您要通过数据集进行分析,那么您已经在削减整体排序时间的性能。
一种可能的解决方案是获取当前排序范围中的第一个、最后一个和中间元素(在快速排序操作期间),并选择中间元素作为枢轴元素。
为了充分分析以决定使用哪种算法,您将要做几乎排序的工作。您可以执行一些操作,例如检查一小部分随机但增加的索引的值(即分析项目的小样本)。
您仍然需要遍历所有记录以确定其是否已排序,因此为了提高性能,请从您的第一条记录开始并运行其余记录,直到您发现某些未正确排序或到达列表末尾。如果您发现未命中,则仅将项目从该位置排序到末尾(因为列表的开头已经排序)。
在第二部分的每个项目中,查看该项目是否比第一部分中的最后一个元素 <,如果是,则仅在第一部分中使用插入排序。否则对第二部分中的所有其他项目进行快速排序。这样,排序就针对特定情况进行了优化。
只有当数据集很大并且已经大部分排序时,快速排序才会出现问题,我会使用以下启发式方法(等待一个完整的解决方案):
如果数据集大小低于阈值,请不要打扰。
如果您可以快速(索引)访问记录(项目),请在每 N 条记录中抽取 1 条记录的样本,并查看它们是否已经排序。对于小样本应该足够快,然后您可以决定是否使用快速排序。
To make a conceptual point that people haven't yet made: Quicksort is a common-sense divide-and-conquer algorithm with an obvious bug in rare cases. Suppose that you want to sort a stack of student papers. (Which I have to do with some regularity.) In the quicksort algorithm, you pick some paper, the pivot. Then divide the other papers according to whether they are before or after the pivot. Then repeat that with the two subpiles. What's the bug? The pivot could be a name that is near one end of the list instead of in the middle, so that it doesn't accomplish much to divide it into two piles.
Merge sort is another divide-and-conquer algorithm that works in a different order. You can merge two sorted lists in linear time. Divide the papers into two equal or nearly equal piles, then recursively sort each one, then merge. Merge sort doesn't have any bugs. One reason that quicksort is more popular than merge sort is historical: Quicksort is fast (usually) and it works without any extra memory. But these days, it can be more important to save comparisons than to save memory, and the actual rearrangement is often abstracted by permuting pointers. If things had always been that way, then I suspect that merge sort would simply have been more popular than quicksort. (And maybe adding "quick" to the name was good salesmanship.)