我尝试了不同的排序算法。我有兴趣真正看到差异。我在一个包含 10 个整数的数组上进行了尝试,一切正常,但是当然,对于少量数据,运行时间可以忽略不计。所以我对 100,000 个整数进行了尝试,这就是我开始注意到哪些整数比其他整数更快的地方。
我相信快速排序比归并排序更快,所以我想知道我的代码中的问题是什么。引用自 ( https://www.youtube.com/watch?v=COk73cpQbFQ&list=PL2_aWCzGMAwKedT2KfDMB9YA5DgASZb3U&index=7 ) 排序算法系列。
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;
}
上面的代码在 100,000 个整数的数组上永远运行,而 mergeSort
在我的计算机上运行 18 秒。
答案 0 :(得分:4)
@sardok 指出您代码中的错误是正确的。这是速度差异的主要解释。但是:
<块引用>我相信众所周知,快速排序比归并排序快
这有点过于简单化了。快速排序和归并排序在各自的领域内都是最优的:分别是不稳定的就地比较排序与稳定的复制比较排序。
对于典型的现实世界数据,您很可能会发现快速排序的执行速度比归并排序快一些,但肯定也存在归并排序会表现更好的数据集。特别是,当所有键都是唯一的并且数据的顺序完全随机时,您可能会发现实现良好的归并排序比实现良好的快速排序要快。
我预测,当您修复了递归错误时,您的快速排序实现仍会比合并排序实现慢一点。这样做的原因是您正在生成一个随机数以选择枢轴;这是一项昂贵的操作。使用数组中的任何单个元素作为枢轴(无论是否随机选择)也被认为是次优的;通过选择数组的第一个、中间和最后一个元素的中值(所谓的三中值快速排序),您将获得更好的性能。
此外,对于小数组,快速排序和归并排序(尤其是快速排序)实际上效率很低。如果在大约 30 个元素的数组大小以下切换到插入排序,您将获得更好的性能(最佳阈值取决于硬件和软件平台,但 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;
}
这更快,因为它避免了交换已经在数组正确一侧的元素。然而,需要记住的是,最初选择的枢轴元素不一定在函数结束时位于 start
;所以这样分区后,partitionIndex
处的元素还没有排序。换句话说,数组的右半部分需要使用 quicksort(num, partitionIndex, end)
而不是 quicksort(num, partitionIndex + 1, end)
进行递归。
通过阅读原版C++ STL implementation by Alex Stepanov,您可以找到很多关于排序算法的启示。
答案 1 :(得分:2)
尽管您的特定实现有其他答案讨论的其他问题,但在判断排序性能时了解一个重要原则很有用。
排序算法性能的简单指标只是简单地计算比较和交换的次数,假设所有比较的成本都相等,并且所有交换的成本都相等。然而,许多现实世界的系统旨在优化某些常见内存操作序列的性能。过去,访问受欢迎或不受欢迎的位置之间可能存在 2:1 或 3:1 的成本差异,但这种差异已经增加到 100:1 或可能更多。因此,以最佳顺序访问事物的排序算法可能会胜过那些没有的排序算法,即使它们最终会执行更多的比较或交换。
答案 2 :(得分: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;
}
这样,我的问题不仅得到了解决,而且我还能够改进我的快速排序算法。以下是我使用过且正在阅读的一些参考资料:
谢谢!