Java 快速排序比合并排序慢?

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];

昨天我正在实现一个快速排序,然后我运行了它,希望运行时比Mergesort(我也实现了Mergesort)更快。我运行了这两个程序,虽然对于较小的数据集10000个元素,快速排序速度更快,但合并排序速度快了4倍多。这是意料之中的,还是我的快速排序代码中有错误

合并排序:

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