使用中间元素作为 Pivot C++ 计算快速排序中的比较次数

Posted

技术标签:

【中文标题】使用中间元素作为 Pivot C++ 计算快速排序中的比较次数【英文标题】:Counting number of comparisons in Quick Sort using middle element as Pivot C++ 【发布时间】:2015-07-20 13:19:37 【问题描述】:

我正在学习斯坦福大学提供的 Coursera 课程:“算法的设计和分析”。以随机顺序提供具有 10000 个不同整数的文本文件。任务是在每次递归调用中使用中间元素作为枢轴执行快速排序后计算比较次数。

这里是文本文件的链接: https://drive.google.com/drive/u/0/folders/0B_WysIAkKMzzN3o4SEhoS0RjMUU

这就是我所做的:

#include <iostream>
#include <fstream>

using namespace std;

int partition(int arr[],int left,int pivot,int right);
int quickSort(int arr[], int left, int right);

int count = 0;

int partition(int arr[],int left,int pivot,int right) 
    int split = left + 1, tmp;

    for (int track = left + 1; track < right; track++) 
        if (arr[track] < pivot) 
            tmp = arr[track];
            arr[track] = arr[split];
            arr[split] = tmp;
            split++;
        
    

    tmp = arr[split - 1];
    arr[split - 1] = arr[left];
    arr[left] = tmp;
    return split - 1;



int quickSort(int arr[], int left, int right) 

    if (right <= left)
        return 0;

    int mid = (right + left - 1)/2;
    int pivot = arr[mid];
    arr[mid] = arr[left];
    arr[left] = pivot;
    int split = partition(arr,left,pivot,right);
    count += right - left - 1;

    quickSort(arr,left,split);
    quickSort(arr,split + 1,right);
    return count;


int main() 

    int ans;
    int arr[10001], i = 0;
    ifstream fin("QuickSort.txt");
    while (fin >> arr[i]) 
        i++;
    
    fin.close();


    ans = quickSort(arr, 0, i);

    //To check if array is sorted
    for(int x = 0;x < i;x++) 
        cout<<arr[x]<<endl;
    
    cout<<endl;

    cout << ans;
    return 0;

虽然数组排序很好,但计数是150657,这是错误的。 有人可以指出我是否在这里遗漏了什么?附言我已经做了好几天了,如果有人帮助我,我将不胜感激!

【问题讨论】:

你为什么要count += right - left - 1; 我需要计算每个递归调用。我假设计数增加了M - 1,其中 M 是子数组的大小。 你确定数组排序正确吗?尝试使用不同的输入数据。您似乎跳过了分区中的第一个元素,这在常见的快速排序中是有意义的,针对随机输入进行了优化,它使用第一个元素作为枢轴。但是您正在使用中间输入位置的数据作为枢轴,因此跳过第一个是行不通的。 是的,我检查过多次。我将继续检查不同的数据集。如果你仔细看,在分区之前,我将中间元素与第一个交换。 int pivot = arr[mid]; arr[mid] = arr[left]; arr[left] = pivot; 我错过了那个exch。所以我现在相信你它分类正确。我仍然看不到直接计数如何给出错误答案,我也看不到直接计数如何与您的原始答案相匹配(正如您所说的那样),在我看来,这似乎算得太多了,也没有直接计数会失败符合我的其他建议。您“正确”比较次数的来源是什么? 【参考方案1】:

当你想计算某物时,直接的方法就是计算你正在计算的东西。据我所知,您想计算以下行的执行次数:

if (arr[track] < pivot)

所以你需要做的就是在该行之前加上++count;。如果您想减少计算和/或了解更多,请考虑执行多少次:

编辑:当我在 --- 下写这些东西时,我真的没有想到。这是错误的。因此,直接计数与您计算计数的原始方法相匹配。我测试了您的代码,现在相信您正在计算正确的计数。试着检查一个小例子,并解释为什么你认为巨型计数是错误的。

我无法访问您的数据文件,因此我无法确认或否认您的程序计算了该文件的正确计数。


for (int track = left + 1; track < right; track++)

该循环最多会执行其主体 (right-left-2) 次。既然你知道 right>left 在那一点上,可以肯定地说(而不是“最多”)right-left-2。

【讨论】:

计算每次比较时,将++count 放在if (arr[track] &lt; pivot) 之前仍然给了我相同的答案:150657。我需要在每次递归调用后将计算保持在最低限度并计数。我还尝试使用count += right - left - 2,它给了我另一个答案:140657,这又是错误的。不过感谢您的尝试。【参考方案2】:

我认为您的计数计算正确。但是全局计数太不合适了,我认为必须纠正。您可以通过以下更改完全消除该变量:

count += right - left - 1;

quickSort(arr,left,split);
quickSort(arr,split + 1,right);
return count;

return
   right - left - 1
+  quickSort(arr,left,split)
+  quickSort(arr,split + 1,right);

或者make count local,改为:

int count = right - left - 1;

count += quickSort(arr,left,split);
count += quickSort(arr,split + 1,right);
return count;

【讨论】:

这两个我都试过了。我仍然得到相同的答案:150657。但我真的很感谢你试图提供帮助!谢谢你。我仍然没有找到解决问题的方法。我会继续努力,直到我做到为止。 正如你上面提到的,有一个“官方答案”和150657不匹配。【参考方案3】:
  count += right - left - 1;

我觉得,这里改为尝试使用“右-左”。由于“right-left+1”实际上是子数组的长度,这将给出您正在寻找的 M-1。希望这可以帮助!

【讨论】:

您好,如果您仔细阅读上面的代码,count += right - left - 1; 是我在我的 quickSort() 函数中使用的。

以上是关于使用中间元素作为 Pivot C++ 计算快速排序中的比较次数的主要内容,如果未能解决你的问题,请参考以下文章

15分钟写不出快速排序的不考虑?!

算法解释

基础排序算法六——快速排序

嵌入式必备 | 详解快速排序算法

如何使快速排序递归?

快速排序