C 快速排序比合并排序慢

C 快速排序比合并排序慢,c,algorithm,sorting,C,Algorithm,Sorting,我在尝试不同的排序算法。我很有兴趣真正了解其中的区别。我在一个包含10个整数的数组上进行了尝试,一切都很好,但当然,在少量数据上,运行时间可以忽略不计。所以我在100000个整数上试过,我开始注意到哪些整数比其他整数快 我相信大家都知道快速排序比合并排序快,所以我想知道我的代码中的问题是什么。引用自()排序算法系列 void quickSort(int num[], int start, int end) { if (start < end) { printf(&q

我在尝试不同的排序算法。我很有兴趣真正了解其中的区别。我在一个包含10个整数的数组上进行了尝试,一切都很好,但当然,在少量数据上,运行时间可以忽略不计。所以我在100000个整数上试过,我开始注意到哪些整数比其他整数快

我相信大家都知道快速排序比合并排序快,所以我想知道我的代码中的问题是什么。引用自()排序算法系列

void quickSort(int num[], int start, int end) {
    if (start < end) {
        printf("\n.");
        int partitionIndex;
        partitionIndex = randomizedPartition(num, start, end);
        quickSort(num, 0, partitionIndex - 1);
        quickSort(num, partitionIndex + 1, end);
    }
}

int partition(int num[], int start, int end) {
    int partitionIndex = start;
    int pivot = end;
    int i;
    for (i = start; i < end; i++) {
        if (num[i] <= num[pivot]) {
            swap(num, i, partitionIndex);
            partitionIndex++;
        }
    }
    swap(num, partitionIndex, pivot);
    return partitionIndex;
}

int randomizedPartition(int num[], int start, int end) {
    int partitionIndex;
    int pivot = (rand() % (end - start + 1)) + start;
    swap(num, pivot, end);
    partitionIndex = partition(num, start, end);
    return partitionIndex;
}
void快速排序(int num[],int start,int end){
如果(开始<结束){
printf(“\n.”);
国际分区指数;
partitionIndex=随机分区(num、start、end);
快速排序(num,0,partitionIndex-1);
快速排序(num,partitionIndex+1,end);
}
}
int分区(int num[],int start,int end){
int partitionIndex=开始;
int pivot=结束;
int i;
for(i=start;i如果(num[i]@sardok指出代码中的错误是正确的。这是速度差异的主要解释。但是:

我相信大家都知道快速排序比合并排序快

这有点过于简单化。快速排序和合并排序在各自的领域内都是最佳的:分别是非稳定的就地比较排序和稳定的复制比较排序

对于典型的真实数据,您可能会发现quicksort的执行速度比mergesort快一点,但也有一些数据集mergesort的执行效果更好。特别是,当所有键都是唯一的且数据顺序完全随机时,您可能会发现实施良好的mergesort比实施良好的mergesort快快速排序

我预测,在修复递归错误后,quicksort实现仍将比mergesort实现慢一点。原因是您生成一个随机数以选择轴;这是一个昂贵的操作。使用数组中的任何单个元素作为轴(无论是否随机选择)也被认为是次优的;通过选择数组的第一个、中间和最后一个元素的中间值(所谓的三个快速排序的中间值),可以获得更好的性能

此外,quicksort和mergesort(尤其是quicksort)对于小型阵列实际上效率很低。如果在大约30个元素的阵列大小以下切换到insertionsort,您将获得更好的性能(最佳阈值取决于硬件和软件平台,但30是一个大致的阈值)

最后,通过将枢轴作为单独的值传递,而不首先将其交换到数组的末尾,并通过从两端迭代,可以加快分区算法的速度:

int partition(int num[], int start, int end, int pivotValue) {
    while (start < end) {
        while (num[start] < pivotValue) ++start;
        while (num[end] > pivotValue) --end;
        swap(num, start++, end--);
    }
    return start;
}
int分区(int-num[],int-start,int-end,int-pivotValue){
while(开始<结束){
而(num[start]pivotValue)——end;
交换(num,start++,end--);
}
返回启动;
}
这会更快,因为它避免了交换已经位于数组正确一侧的元素。但是,需要记住的是,最初选择的枢轴元素在函数结束时不一定位于
开始处
;因此,以这种方式进行分区后,
分区索引处的元素不进行排序换句话说,数组的右半部分需要使用
quicksort(num,partitionIndex,end)
而不是
quicksort(num,partitionIndex+1,end)
递归


通过阅读原文,您可以发现有关排序算法的许多启示。

尽管您的特定实现有其他答案中讨论的其他问题,但在判断排序性能时理解一个重要原则是很有用的


简单的排序算法性能指标简单地计算比较和交换的数量,前提是所有比较都具有相同的成本,所有交换都具有相同的成本。然而,许多现实世界的系统都是为了优化某些常见内存操作序列的性能而设计的;而过去的情况是:访问有利位置和非有利位置之间的成本差异不是2:1或3:1,这种差异已经增加到100:1或可能更大。因此,以最佳顺序访问对象的排序算法可能会优于那些不以最佳顺序访问对象的排序算法,即使它们最终执行更多的比较或交换。

除了我之外,每个人都帮了很多忙希望包括我所做的更正:

首先,主要的错误是我使用了
0
而不是
start
,所以我替换了它。 接下来,我用了“三的中位数”。 然后,由于@Julian,我的分区最终得到了以下代码行:

int partition3(int num[], int start, int end, int pivotValue){
   int left = start;
   int right = end - 1;
   while(left <= right){
       while(num[left] <= pivotValue && left <= right) ++left;
       while(num[right] >= pivotValue && left <= right) --right;
       if(left < right) swap(num, left++, right--);
   }
   swap(num, left, end);
   return left;
int分区3(int-num[],int-start,int-end,int-pivotValue){
int左=开始;
int right=end-1;

while(左)我认为您对这一行有问题:
quickSort(num,0,partitionIndex-1);
。在我看来,您应该将
0
替换为
start
“永远”听起来像是一个bug。测试你的代码!确保你的快速排序实现是正确的。如果你有bug,那么度量值就没有意义了。@sardok我知道开始值应该是0,对吗?因为左侧将被下一个分区。@krom,想象一个有100个数字的数组。假设右侧分区从50开始。现在,这个分区on将再次拆分为两个分区。你认为嵌套的左分区必须从何处开始?这很值得学习,但非常有教育意义!感谢你的解释!我将继续分析你所说的内容并尝试你的建议。关于随机分区,我观看的youtube视频说它实际上可以防止最坏的情况发生O(n^2)的时间复杂度。遗憾的是,我当前的代码仍然崩溃,因此我无法将其与您建议的进行比较。我会回来讨论