zoukankan      html  css  js  c++  java
  • 快速排序

    快速排序

    我居然现在还不会快排。

    第一种写法:

    // 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。

    然而,后来我又在寻思如何用第二种写法求第k小数。我原本以为第二种写法会漏过与基准相同的数,但这其实一点关系都没有,不会影响结果(仔细想一想)。不过第二种写法还是比较难写的。

    还有,第一种写法是可以改进的。只要找出等于基准值的区间,然后取中值即可。虽然较慢(常数在第二种写法的1.5倍左右),但是个人很喜欢这种写法。

    #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+r)>>1], now=l;
        for (int i=l; i<r; ++i) //比std小的都排到左端
            if (a[i]<std) swap(a[i], a[now++]);
        int tmp=now; //[l,tmp)是比std小的
        for (int i=tmp; i<r; ++i) //和std相等的都排到右端
            if (a[i]==std) swap(a[i], a[now++]);
        //[tmp,now)是和std相等的
        sort(l, tmp); sort(now, 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;
    }
    
  • 相关阅读:
    APP端自动化 之 Windows-Android-Appium环境搭建
    Python3学习笔记-继承、封装、多态
    Python3学习笔记-构造函数与析构函数
    多线程同时操作界面使用互斥体
    AX2009 C#客户端通过Web service批量审核工作流(一)
    AX2009报表发送邮件(二)
    AX2009报表发送邮件(一)
    AX2012分页显示数据
    AX2012 菜单根据不同公司动态显示
    AX2012使用域用户组
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/8448165.html
Copyright © 2011-2022 走看看