快速排序算法(三种分区方法要熟练!)

快排确实厉害!!!

总的思想是分治递归,取定一个值作为标签,比该值小的去左边,比该值大的去右边。

单向扫描分区法:

快速排序算法(三种分区方法要熟练!)

去左边的操作:只将sp++即可

去右边的操作:具体是将sp指向的值与bigger指向的值交换

考虑边界:当扫描指针sp与bigger相等时,再执行一次循环后,sp刚好在bigger的右边一格。

快速排序算法(三种分区方法要熟练!)

/*
 * 快速排序算法
 */
void swp(int arr[], int n, int m)
{
    int temp = arr[n];
    arr[n] = arr[m];
    arr[m] = temp;
}
int Part(int arr[], int p, int r)
{
    int pivot = arr[p]; // 定中间数
    int sp = p + 1;   // 扫描指针
    int bigger = r;   // 右侧指针
    while (sp <= bigger)
    {
        if (arr[sp] > pivot) {
            swp(arr, sp, bigger);
            bigger--;
        }
        else
            sp++;
    }
    swp(arr, p, bigger);
    return bigger;
}
void quickSort(int arr[], int p, int r)
{
    int q = 0;
    if (p < r) {
        // 分区:小于的数移动到左边,大于的数移动到右边
        q = Part(arr, p, r);
        // 将排序问题分治
        quickSort(arr, p, q - 1);
        quickSort(arr, q + 1, r);
    }
}
int main()
{
    int ar[2] = {1, -1};
    quickSort(ar, 0, 1);
    // 打印
    for (int i = 0; i <= 1; i++)
        cout << ar[i] << endl;
    return 0;
}

双向扫描分区法:

与单向扫描分区类似,但left指针一直往右移,直到大于中间值时停止;right指针一直往左移,直到小于中间值时停止。然后left值与right值交换。之后left继续一直左移,right一直右移。重复执行。

小心:left一直左移(或right一直右移)导致的数组越界问题。

/*
 * 快速排序算法
 */
void swp(int arr[], int n, int m)
{
    int temp = arr[n];
    arr[n] = arr[m];
    arr[m] = temp;
}
int Part(int arr[], int p, int r)
{
    int pivot = arr[p]; // 定中间数
    int left = p + 1;  // 左指针
    int right = r;     // 右指针
    // 双向扫描核心
    while (left <= right)
    {
        while (left <= right && arr[left] <= pivot) left++;
        while (left <= right && arr[right] > pivot) right--;
        if (left < right)
            swp(arr, left, right);
    }
    swp(arr, p, right);
    return right;
}
void quickSort(int arr[], int p, int r)
{
    int q = 0;
    if (p < r) {
        // 分区:小于等于的数移动到左边,大于的数移动到右边
        q = Part(arr, p, r);
        // 将排序问题分治
        quickSort(arr, p, q - 1);
        quickSort(arr, q + 1, r);
    }
}
int main()
{
    int ar[8] = {2, 3, 3, 3, 45, 8, 4, 6};
    quickSort(ar, 0, 7);
    // 打印
    for (int i = 0; i <= 7; i++)
        cout << ar[i] << " ";
    return 0;
}

三指针分区法:

快速排序算法(三种分区方法要熟练!)

三指针分区法对于相等元素较多的数组能提升一定的效率。

Next_Less_Pos指针始终指向数组的相等区第一个元素;Next_Scan_Pos指针始终指向要扫描区域的第一个元素;Next_Bigger_Pos指针的右区域所有元素总大于主元。

快速排序算法(三种分区方法要熟练!)

/*
 * 快速排序算法
 */
void swp(int arr[], int n, int m)
{
    int temp = arr[n];
    arr[n] = arr[m];
    arr[m] = temp;
}
void Part(int arr[], int p, int &e, int &bigger)
{
    int pivot = arr[p]; // 定中间数
    int s = e;
    // 三指针分区核心
    while (s <= bigger) {
        if (arr[s] < pivot) { 
            swp(arr, s, e);
            s++; e++;
        }
        else if (arr[s] > pivot) {
            swp(arr, s, bigger);
            bigger--;
        }
        else s++; 
    }
    swp(arr, e - 1, p);
}
void quickSort(int arr[], int p, int r)
{
    int left = p + 1, right = r;
    if (p < r) {
        // 分区:小于的数移动到左边,大于的数移动到右边,等于的数在中间
        Part(arr, p, left, right);
        // 将排序问题分治
        if (left > p) quickSort(arr, p, left - 1);
        if (right < r) quickSort(arr, right + 1, r);
    }
}
int main()
{
    int ar[20];
    srand((unsigned)time(nullptr));
    for (int i = 0; i <= 19; i++)
        ar[i] = rand() % (10 - 1 + 1) + 1;
    quickSort(ar, 0, 19);
    // 打印
    for (int i = 0; i <= 19; i++)
        cout << ar[i] << " ";
    return 0;
}

小心边界:必须是相等区域的前一个小于元素,和相等区域的后一个大于元素(即 left - 1 和 right + 1),这时能避免无限循环。

但在避免无限循环的同时,又得保证边界下标的合理性,故我们此时加 if 语句判断。