Java 快速排序比合并排序慢?
昨天我正在实现一个快速排序,然后我运行了它,希望运行时比Mergesort(我也实现了Mergesort)更快。我运行了这两个程序,虽然对于较小的数据集10000个元素,快速排序速度更快,但合并排序速度快了4倍多。这是意料之中的,还是我的快速排序代码中有错误 合并排序:Java 快速排序比合并排序慢?,java,algorithm,quicksort,mergesort,Java,Algorithm,Quicksort,Mergesort,昨天我正在实现一个快速排序,然后我运行了它,希望运行时比Mergesort(我也实现了Mergesort)更快。我运行了这两个程序,虽然对于较小的数据集10000个元素,快速排序速度更快,但合并排序速度快了4倍多。这是意料之中的,还是我的快速排序代码中有错误 合并排序: public static void mergeSort(int[ ] e) { if (e.length <= 1) return; int[] first = new int[e.length/2];
public static void mergeSort(int[ ] e)
{
if (e.length <= 1) return;
int[] first = new int[e.length/2];
int[] second = new int[e.length - first.length];
System.arraycopy(e, 0, first, 0, first.length);
System.arraycopy(e, first.length, second, 0, second.length);
mergeSort(first);
mergeSort(second);
System.arraycopy(merge(first, second), 0, e, 0, e.length);
}
private static int[] merge(int[] first, int[] second) {
int iFirst = 0;
int iSecond = 0;
int iCombined = 0;
int[] combined = new int[first.length + second.length];
while(iFirst < first.length && iSecond < second.length) {
if (first[iFirst] > second[iSecond]) {
combined[iCombined++] = second[iSecond++];
}
else combined[iCombined++] = first[iFirst++];
}
for(; iFirst < first.length; iFirst++) {
combined[iCombined++] = first[iFirst];
}
for(; iSecond < second.length; iSecond++) {
combined[iCombined++] = second[iSecond];
}
return combined;
}
公共静态无效合并排序(int[]e)
{
if(例如,长度秒[iSecond]){
组合的[i组合+]=第二个[i秒++];
}
else组合[iCombined++]=第一个[iFirst++];
}
对于(;iFirst
快速排序:
public static void quicksort(int[] a, int first, int last) {
if (first >= last) return;
int partitionIndex = partition(a, first, last);
quicksort(a, first, partitionIndex - 1);
quicksort(a, partitionIndex + 1, last);
}
public static int partition(int[] x, int first, int last) {
int left = first;
int right = last;
int pivot = x[first];
int pivotIdx = first;
while(left <= right) {
while(left < x.length && x[left] <= pivot) left++;
while(right >= 0 && x[right] > pivot) right--;
if (left <= right) {
int temp = x[left];
x[left] = x[right];
x[right] = temp;
}
}
pivotIdx = right;
x[first] = x[right];
x[pivotIdx] = pivot;
return pivotIdx;
}
publicstaticvoidquicksort(int[]a,int-first,int-last){
如果(第一次>=最后一次)返回;
int partitionIndex=分区(a,第一个,最后一个);
快速排序(a,第一,分区索引-1);
快速排序(a,分区索引+1,最后一个);
}
公共静态int分区(int[]x,int first,int last){
int左=第一;
int right=last;
int pivot=x[第一];
int pivotIdx=第一;
而(左枢轴)右--;
如果(左之前在SO上讨论过):“
~基于此维基百科,您的结果是预期的。我可以想象,通过直接访问内存,例如使用C,可以比使用Mergesort更有效地提高快速排序的性能
另一个原因是Mergesort需要更多的内存,因为很难将其实现为就地排序
特别是对于您的实现,您可以改进枢轴的选择,有许多不同的算法可以找到一个好的枢轴
可以看出,可以用不同的方式实现快速排序。您的数据集是否足够随机?它们是否部分排序
这可能会影响排序的速度
与快速排序的partition()一样,如果数字是按顺序排序的,则可以跳过,直到找到一个不按顺序排序的为止。这可能取决于您为测试排序的数据类型(已排序的列表、随机化的、反向排序的)。此外,如果您选择一个随机轴而不是使用第一个元素,则快速排序通常可能会更快。合并排序的最坏情况是快速排序的平均情况,因此,如果您没有良好的实现,则合并排序总体上会更快。快速排序的目的是避免次平均情况。选择更好的pivot(中位数-of-3有帮助),您将看到不同之处。对于相对较小的数组大小,快速排序的优势之一只是硬件实现的产物
在阵列上,可以就地进行快速排序,这意味着您正在读取和写入同一内存区域。另一方面,Mergesort通常需要分配新的缓冲区,这意味着您的内存访问更分散。您可以在示例实现中看到这两种行为
因此,对于相对较小的数据集,快速排序更有可能获得缓存命中,因此在大多数硬件上运行速度更快
正如您的实验所证实的那样,对于大型数据集或其他数据结构(如链表),Mergesort仍然是一个非常好的解决方案。我实际上刚刚用C编写了一个“链表比较排序演示程序”,并得出了类似的结论(Mergesort将在大多数应用中击败quicksort),尽管我被告知快速排序通常不会用于链表。我要注意的是,轴值的选择是一个非常重要的因素——我的初始版本使用了一个随机节点作为轴,当我对其进行改进时,取平均值为2(随机)节点,1000000个记录的执行时间从超过4分钟到小于10秒,使其与归并保持一致。
合并排序和快速排序具有相同的大O最佳情况(n*log(n))不管人们怎么说,“大O”实际上是关于迭代计数,而不是比较计数。两者之间产生的最大差异总是对快速排序不利,它涉及到已经基本排序或包含大量关系的列表(当quicksort比mergesort做得更好时,差别不会太大)。这是因为关联或已排序的段直接通过mergesort进行简化;当两个拆分列表返回合并时,如果一个列表已包含所有较小的值,则左侧的所有值将一次一个地与右侧的第一个元素进行比较,然后(因为返回的列表具有内部顺序)不需要做进一步的比较,右边的只需迭代到末尾。也就是说,迭代次数将保持不变,但比较次数将减少一半。如果您谈论的是实际时间并对字符串进行排序,那么比较的成本就很高
如果未仔细确定轴心值,则快速排序中的关联和已排序的段很容易导致列表不平衡,并且列表不平衡(例如,右侧一个,左侧十个)因此,如果你能让你的快速排序在一个已经排序的列表上像在ramdominized列表上一样执行,你就有了一个很好的方法来找到轴心
如果您感兴趣,演示程序将生成如下输出:
[root~/C] ./a.out -1 3
Using "", 0 records
Primary Criteria offset=128
Command (h for help, Q to quit): N
How many records? 4000000
New list is 562500.00 kb
Command (h for help, Q to quit): m
Mergesorting..............3999999 function calls
123539969 Iterations Comparison calls: 82696100
Elapsed time: 0 min 9 sec
Command (h for help, Q to quit): S
Shuffled.
Command (h for help, Q to quit): q
Quicksorting..............4000000 function calls
190179315 Iterations Comparison calls: 100817020
Elapsed time: 0 min 23 sec
虽然没有krazy kolors,但在半路上我还有一些关于它的东西
ps.这两种排序都不需要链接列表的额外内存。(1)有一个由C qsort()使用的qsort algo,它不需要额外内存。此
很可能是霍尔发明的。这使得qsort()在C中速度很快
(2) 在运行qsort之前随机化数据几乎总是会加快i
2 MB, time_diff 165.156000 ms, 78.752518 ns per byte
4 MB, time_diff 344.298000 ms, 82.087040 ns per byte
8 MB, time_diff 730.926000 ms, 87.133169 ns per byte
16 MB, time_diff 1541.215000 ms, 91.863573 ns per byte
32 MB, time_diff 3088.924000 ms, 92.057109 ns per byte
64 MB, time_diff 6262.868000 ms, 93.324006 ns per byte
128 MB, time_diff 12887.018000 ms, 96.015766 ns per byte
256 MB, time_diff 26731.597000 ms, 99.582959 ns per byte
2 MB, time_diff 243.519000 ms, 116.118908 ns per byte
4 MB, time_diff 504.975000 ms, 120.395422 ns per byte
8 MB, time_diff 1075.276000 ms, 128.182888 ns per byte
16 MB, time_diff 2183.865000 ms, 130.168498 ns per byte
32 MB, time_diff 4343.993000 ms, 129.461080 ns per byte
64 MB, time_diff 8714.166000 ms, 129.851192 ns per byte
128 MB, time_diff 17881.344000 ms, 133.226395 ns per byte
256 MB, time_diff 36751.029000 ms, 136.908252 ns per byte