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

Counting number of comparisons in Quick Sort using middle element as Pivot C++

本文关键字:快速排序 计算 比较 C++ 透视 中间 元素      更新时间:2023-10-16

我正在上斯坦福大学提供的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,这是错误的。有人能指出我是否遗漏了什么吗?附言:我已经做了好几天了,所以如果有人帮我,我会非常感激的!

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

if (arr[track] < pivot)

所以你所需要做的就是把++count;放在这行前面。如果你想做更少的计算和/或了解更多,仔细想想它被执行了多少次:

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

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


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

该循环最多执行其正文(右-左-2(次。既然你在这一点上知道右>左,那么准确地说(而不是"最多"(右-左-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);

或本地计数,更改为:

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

我觉得,在这里尝试使用"右-左"。由于"右-左+1"实际上是子数组的长度,这将给出您正在寻找的M-1。希望这能有所帮助!

用于选择中间元素的中间索引不正确。你没有考虑到这样一个事实,即你将左和右作为数组中的某些点发送,而不是作为0和n。例如,如果你有[5,4,2,1,6,7],并且你发送这个数组的右半部分,那么你的左将是3,右将是5(索引(。这个数组的中值是6,但这个数字的索引实际上是4。这里的行返回的是(5+3-1(/2=3,这是不正确的。注:我假设权利是你们最后的要素。

int mid = (right + left - 1)/2;

试着这样做吧,让你的数组接受左边为0,右边为数组的长度

int mid = ceil((right-left)/2) - 1 + left

你也可以使用旧的(right+left-1(/2的方式,但一定要做一次ceil并仔细检查,看看你是否真的选择了正确的中值。