[模板]快速排序

原题链接:acwing785.快速排序

给定数组大小n和数组q[n],对数组进行排序。

快速排序的原理:基于分治

(1)确定分界点x:可以取x为q[l], q[l + r >> 1],q[r], 或者数组中间随机一个值(建议取q[l + r >> 1], 原因在后面说)。这里l和r是当前待排序数组的左右端点。

(2)重新调整区间,使得调整后的区间,小于等于x的数都在x的左边,大于等于x的数都在x的右边(和x相等的数在左边、右边都可)。 调整区间的方法有两种,后面说。

(3)递归处理分界点的左右两段,最后拼接起来的区间就是有序的了。

调整区间的方法(一):

(1)额外开两个数组a[], b[]

(2)扫描数组q[l~r],如果当前的数q[i] <= x, 则把q[i]存入数组a. 即q[i] -> a[];
如果当前的数q[i]>=x,则把q[i]存入数组b. 即q[i] -> b[i];

(3)此时a[], b[]就分别是q[]的左半部分和右半部分了,把它们的值赋给q[]. 即a[] -> q[], b[] -> q[]。

这种方法的优点是好理解。缺点也显而易见:空间开销大。

调整区间的方法(二):

有一种用双指针来调整区间的优雅的方法。

(1)一开始,我们让两个指针i和j分别指向区间的两个端点l和r;

(2)当q[i] < x时,i向右移动,若找到q[i] >= x,则i停下。 j向左移动,直到i和j相遇或者q[j] <= x;

(3)若i和j未相遇,则i和j此时指向的元素都是错位的,交换这两个数的位置:swap(q[i], q[j]);

(4) 重复上面的步骤,知道i和j相遇。

这种划分区间方法的正确性证明:在任意时刻,左指针i左边的数都是小于等于x的,j右边的数都是大于等于x的,当i与j相遇时,
相遇点左边的数都小于等于x,相遇点右边的数都大于等于x,故区间调整正确。

这题代码如下:

#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int q[N];

void quick_sort(int q[], int l, int r) {
    if(l >= r) {                              //如果区间没有数,或者只有一个数,直接return
        return ;
    }
    int x = q[l + r >> 1], i = l - 1, j = r + 1;      //分界点x最好取中位数q[l + r >> 1],因为如果取边界q[l]或者q[r],且区间是升序或降序时,时间复杂度为O(n^2),在这题会超时
    while(i < j) {
        do i++; while(q[i] < x);
        do j--; while(q[j] > x);
        if(i < j) {
            swap(q[i], q[j]);
        }
    }
    quick_sort(q, l, j);                              //递归处理左半部分区间
    quick_sort(q, j + 1, r);                          //递归处理右半部分区间
}

int main() {
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; ++i) {
        scanf("%d", &q[i]);
    }
    quick_sort(q, 0, n - 1);
    for(int i = 0; i < n; ++i) {
        printf("%d ", q[i]);
    }
    return 0;
}
posted @ 2020-08-11 11:20  machine_gun_lin  阅读(158)  评论(0编辑  收藏  举报