Hoare 分区如何在 QuickSort 中工作?

How does Hoare partitioning work in QuickSort?

这是直接来自书本 (CORMEN) 的伪代码:

Partition(A,p,r)
        x=A[p]
        i=p-1
        j=r+1 
        while(TRUE)
            repeat
                j=j-1
            until A[j]<=x
            repeat
                i=i+1
            until A[i]>=x
            if i<j
                SWAP A[i] <=> A[j]
            else return j

这是 C++ 中的代码:

#include<bits/stdc++.h>
using namespace std;


int partition(int a[], int low, int high)
{
    int pivot = a[low];
    int i = low - 1;
    int j = high + 1;
    while (1)
    {
        do {
            i++;
        } while (a[i] < pivot);

        do {
            j--;
        } while (a[j] > pivot);

        if (i >= j) {
            cout<<j<<endl;
            return j;

        }

        swap(a[i], a[j]);
    }
}


/* The main function that implements QuickSort
arr[] --> Array to be sorted,
low --> Starting index,
high --> Ending index */
void quickSort(int arr[], int low, int high)
{
    if (low < high)
    {
        /* pi is partitioning index, arr[p] is now
        at right place*/
        int pi = partition(arr, low, high);

        // Separately sort elements before
        // partition and after partition
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

/* Function to print an array */
void printArray(int arr[], int size)
{
    int i;
    for (i=0; i < size; i++)
        printf("%d ", arr[i]);
    printf("\n");
}

// Driver program to test above functions
int main()
{
    int arr[] = {7,3,2,6,4,1,3,5};
    int n = sizeof(arr)/sizeof(arr[0]);
    cout<<"partition:\n";
    partition(arr,0,7);
    printArray(arr, n);

    quickSort(arr, 0, n-1);
    printf("Sorted array: \n");
    printArray(arr, n);
    return 0;
}

如果我在输入中使用这个数组:

[5,3,2,6,4,1,3,7]

逻辑上一切正常,因为分区 return 数组将是:

[3,3,2,1,4,6,5,7] 

终止 i=5 和 j=4 所以我的主元是 4。4 左边的所有元素都是次要的,右边的都是主要的

现在如果我在输入中使用这个数组:

[7,3,2,6,4,1,3,5]

我在分区的最后会有这种情况

[5,3,2,6,4,1,3,7]

这对我来说 return 是枢轴 j = 6,即 3。现在 3 左边的元素并不都是次要的,右边的元素都是主要的。 但这怎么可能有效呢?我不应该把元素放在主次要的左边和主要的右边吗?

使用 Hoare 分区,主元和等于主元的值可以在任何地方结束。返回的索引不是枢轴的索引,而只是一个分隔符。对于上面的代码,当分区完成后,elements <= pivot 将在 j 或左侧,而 elements >= pivot 将在 j 右侧。做一个分割步骤后,C++代码应该是:

        quickSort(arr, low, pi);           // not pi - 1
        quickSort(arr, pi + 1, high);

包含快速排序测试的示例代码:

uint32_t Rnd32()
{
static uint32_t r = 0;
    r = r*1664525 + 1013904223;
    return r;
}

int Partition(int ar[], int lo, int hi)
{
    int pv = ar[lo+(hi-lo)/2];
    int i = lo - 1;
    int j = hi + 1;
    while(1){
        while(ar[++i] < pv);
        while(ar[--j] > pv);
        if(i >= j)
            return j;
        std::swap(ar[i], ar[j]);
    }
}

void QuickSort(int ar[], int lo, int hi)
{
    while (lo < hi){
        int pi = Partition(ar, lo, hi);
        if((pi - lo) < (pi - hi)){
            QuickSort(ar, lo, pi);
            lo = pi + 1;
        } else {
            QuickSort(ar, pi + 1, hi);
            hi = pi;
        }
    }
}

#define COUNT (16*1024*1024)

int main(int argc, char**argv)
{
    size_t i;
    int * ar = new int [COUNT];
    for(i = 0; i < COUNT; i++){
        ar[i] = Rnd32();
    }

    QuickSort(ar, 0, COUNT-1);

    for(i = 1; i < COUNT; i++)
        if(ar[i-1] > ar[i])
            break;
    if(i == COUNT)
        std::cout << "passed" << std::endl;
    else
        std::cout << "failed" << std::endl;

    delete[] ar;

    return(0);
}