首页 > 编程语言 > 详细

快速排序

时间:2018-02-14 13:15:57      阅读:208      评论:0      收藏:0      [点我收藏+]

快速排序

我居然现在还不会快排。

第一种写法:

// luogu-judger-enable-o2
#include <cstdio>
using namespace std;

const int maxn=1e5+5;
int n, a[maxn];

void swap(int &a, int &b){
    int tmp=a; a=b; b=tmp; }

void sort(int l, int r){ // 代表[l,r)
    //两哨兵太难写了,一哨兵大X好
    if (l+1>=r) return;
    int std=a[l], now=l;
    for (int i=l+1; i<r; ++i) //比std小的都排到左端
        if (a[i]<std) swap(a[i], a[++now]); //能取等号?
    swap(a[l], a[now]);
    sort(l, now); sort(now+1, r); //now+1,避免死循环
}

int main(){
    scanf("%d", &n);
    for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
    sort(1, n+1);
    for (int i=1; i<=n; ++i) printf("%d ", a[i]);
    return 0;
}

这种单哨兵的方法,和双哨兵且有一个哨兵带等号的方法一样,都有一个致命缺陷——当序列中数字全部相同时,时间退化到n^2。这就是交这个程序到洛谷上tle的原因。于是我找到了写法二——

#include <cstdio>
using namespace std;

const int maxn=1e5+5;
int n, a[maxn];

void swap(int &a, int &b){
    int tmp=a; a=b; b=tmp; }

void sort(int l, int r){ //左闭右开区间
    if (l+1>=r) return;
    int std=a[(l+r)>>1];
    int i=l, j=r-1;
    while (i<=j){ //避免撞车使i和j没有到预定位置,加上等号
        //注意两个while不能带等号,不然i或j可能一冲到底
        //,造成区间大小不变,陷入死循环
        while (a[i]<std) ++i;
        while (a[j]>std) --j;
        //可能i和j都冲过头了,要避免这种情况下交换
        if (i<=j){ //同理避免撞车,加上等号
            swap(a[i], a[j]);
            //若来到了与基准值相等的区域,就需要用蛮力推进了
            ++i; --j;
        }
    }
    sort(l, j+1); sort(i, r);
}

int main(){
    scanf("%d", &n);
    for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
    sort(1, n+1);
    for (int i=1; i<=n; ++i) printf("%d ", a[i]);
    return 0;
}

大部分注意点但都写在代码中了,我来解释一下。首先,用左闭右开区间,是因为看了刘汝佳的书,书中说这种区间表示法更好。while中不能带等号,不然要么死循环,要么超时。由于区间的划分时按照i和j来决定的,所以i必须正好在基准值右侧,j必须正好在基准值左侧,才能保证在长度为二的区间中不出现循环情况。因此,i<j的判定要加上等号,这样j一定小于i。哎,快排真是最难写的算法。

快速排序

原文:https://www.cnblogs.com/MyNameIsPc/p/8448165.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!