快速排序算法中的快速排序函数是如何工作的?

3

我已经理解了快速排序算法中的分区部分,但是我在理解快速排序递归函数方面遇到了困难。请问有人能够逐步解释它是如何工作的吗? 我在此贴出C++代码。

using namespace std;

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int partition(int myArray[], int low, int high) {
    int i = (low - 1);
    int pivot = myArray[high];

    for (int j = low; j <= high - 1; j++) {
        if (myArray[j] < pivot) {
            i++;
            swap(&myArray[i], &myArray[j]);
        }
    }
    swap(&myArray[i + 1], &myArray[high]);
    return (i + 1);
}

void quickSort(int myArray[], int low, int high) {

    if (low < high) {
        int pi = partition(myArray, low, high);

        quickSort(myArray, low, pi - 1);
        quickSort(myArray, pi + 1, high);
    }
}

void cinArray(int myArray[], int n) {
    for (int p = 0; p < n; p++) {
        cin >> myArray[p];
    }
}

void print(int myArray[], int n) {
    for (int z = 0; z < n; z++) {
        cout << myArray[z] << "\t";
    }
}

int main() {
    int myArray[10];
    int size = sizeof(myArray) / sizeof(myArray[0]);
    cout << "Write 10 numbers: ";
    cinArray(myArray, size);
    quickSort(myArray, 0, size - 1);
    print(myArray, size);
    return 0;
}

我的逻辑(逐步)如下:

  1. if (low < high)总是为true。第一次时,从int main中取出low(0)和high(9)的值。
  2. pi将等于partition函数的返回值(i+1),我假设为了返回该值,函数必须先运行。
  3. 我们调用quicksort函数两次,为函数提供新的参数。一次为i+1之前的值,一次为原始分区后面的值。我想重点关注第一个函数调用,即i+1之前的值发生了什么。
  4. 函数再次启动,if语句为真(总是为真),pi调用partition函数并返回i+1,pi等于i+1。如果此时值仍未排序怎么办?我想快速排序函数会重新开始(感觉像个循环)。但由于IF语句永远为真,这种循环情况何时会停止?
  5. 此外,假设我在第4点的逻辑是正确的,那么代码第一次如何运行?它从第一个quickSort(myArray, low, pi - 1);函数调用开始循环,直到有东西停止它,然后对第二个调用quickSort(myArray, pi + 1, high);进行相同的操作吗?还是先对i+1之前的值进行分区,然后对i+1之后的值进行分区,然后重新启动函数?

我知道这是一个基本问题,但我真的很难理解这个算法。

2个回答

3

if (low < high)将始终为真。

不正确。在第一次调用时,它将为真,但是QuickSort会使用递归方式调用自身,并且lowhigh之间的间隔逐渐变小。这个if语句是算法最终终止的原因 - 你在下面询问了这个问题。

Pi将等于partition函数的返回值(i+1)

正确。 pi是pivot index的缩写,即所选枢轴在分区后最终停留的位置。

那么如果此时值仍未排序怎么办?

在分区后,您知道左侧分区中的任何值都不大于枢轴值,并且右侧分区中的任何值都不小于枢轴值。这就是您所知道的全部内容,也是算法需要知道的全部内容,以便最终成功。每个分区被递归地分区,直到其中只有一个元素。

循环情况何时停止?

请参见我的第一点。


当一个分区(比如说分区A)再次被分成两个更小的分区(A1和A2),这两个分区都需要再次进行分区,直到像你所说的那样,元素数量太少无法满足IF语句的条件。在第一个分区A被分成两个更小的分区之后,递归调用会计算这两个分区吗?(对于比枢轴值小和大的值) 更好地解释一下,代码将按照以下方式工作:1)运行A1的调用函数2)运行A2的调用函数?还是代码处理A1直到它无法再次分区,然后转移到A2? - xJason
据我所知,机器会尽可能地深入探究以获取答案,然后再继续前进。这就是为什么我的最初印象是A1将被分区直到无法再分,但是A2会发生什么呢?它会跳回去吗?因此,主要是代码处理的顺序使我不理解这个过程。(还要感谢您的回答) - xJason
1
您说得对,快速排序使用深度优先的方法,在初始分区后,左侧分区通过递归完全排序,然后才处理右侧分区。 - 500 - Internal Server Error

2
分区函数将枢轴元素放置在正确的位置并返回其索引。接下来的两个调用都排除了该元素,最坏情况下,一个调用将是零个元素,另一个调用将是n-1个元素。继续最坏情况,每个递归级别传递的大小仅减少1个元素,时间复杂度为O(n^2)。最好的情况是,如果枢轴最终位于中间,在每个递归级别上都有相等的分割,或者接近相等的分割,时间复杂度为O(n log(n))。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接