• 基数排序


    基数排序是一种高效的排序算法,多用于处理字符串(不支持负数,实数排序效率不高)

    算法流程

    · 基数排序就是以每一位的数值为关键字来排序,也就是说,是按位排序(一般从低位向高位枚举)

    inline void Qsort(){
        for(int i=0;i<M;++i) b[i]=0;// b[i]表示 i 这个数值出现了多少次
        for(int i=1;i<=n;i++) b[(a[i]/base)%10]++,rank[i]=0;//统计所有数当前位的数值个数
        for(int i=1;i<M;i++) b[i]+=b[i-1];//做前缀和
        for(int i=n;i>=1;i--)
            rank[b[(a[i]/base)%10]--]=a[i];//硬核排序
        for(int i=1;i<=n;i++) a[i]=rank[i];//把排好序的数赋给原数组
    }
    
    inline void sort(){
        int max=0;
        for(int i=1;i<=n;i++)
            if(a[i]>max) max=a[i]; //统计最大值,最大值的位数决定了此次基数排序需要枚举的位数
        while(max/base){//如果当前位没有大于最大数的最高位,说明排序没有完成
            Qsort();//基数排序
            base=(base<<1)+(base<<3);//枚举下一位
        }
    }
    

    样例

    8
    54 23 674 12 544 32 9 142
    

    首先,按个位为关键字排序,统计数值
    b 0 0 3 1 3 0 0 0 0 1(例如其中位于第 3 个数的那个 3 就表示 “2” 这个值出现了 3 次,如图)

    for(int i=n;i>=1;i--)
        rank[b[(a[i]/base)%10]--]=a[i];
    

    (a[i]/base)%10 表示当前位的数值
    b[(a[i]/base)%10] 表示当前位的数值的个数,但是刚刚我们做了前缀和,它的意义就变成了小于等于这个数值的数有多少个
    小于等于这个数值的数的个数,不就是当前数的排名吗?!所以我们直接把它的排名赋成这个值。

    于是序列就变成了

    接下来来到第二位 十位
    同样的,统计个数 b 1 1 1 1 2 1 0 1 0 0
    我们发现 0 这个数值的一位累计了 1,这是怎么回事,似乎没有 0 啊? 考虑到有一个数是 9 ,它没有十位了,于是十位就是 0 (前导零)。这样对排序有什么影响吗? 因为这一位的数值为 0 ,肯定是最小的,排在最前面,与正确排序结果相同
    如图:

    第二位排序后:

    以此类推,最后的排序结果为:

    为什么要倒序赋值呢?

    假设我们现在正在排十位,显然个位已经是排好的。我们现在正在枚举序列中的第 i 个数,假设这个数的十位的数值之前在枚举 i+1 到 n 的数的时候就出现过,位置为 j ,那么显然 a[j] 的个位是大于 a[i] 的个位的(个位上次排序已排好且为升序),再回到 rank[b[(a[i]/base)%10]--]=a[i] 中的 b[···]--,也就是说当前十位相同的数在序列中的位置是相邻的, a[i] 现在刚好排在了 a[j] 的前一位,又有 a[i]<a[j] ,满足升序。

    #include<stdio.h>
    #define M 10
    #define N 100007
    
    template<class T>
    inline void read(T &x){
        T flag=1;x=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        x*=flag;
    }
    
    int a[N],base=1,n,b[M]={0},rank[N];
    inline void Qsort(){
        for(int i=0;i<M;++i) b[i]=0;
        for(int i=1;i<=n;i++) b[(a[i]/base)%10]++,rank[i]=0;
        for(int i=1;i<M;i++) b[i]+=b[i-1];
        for(int i=n;i>=1;i--)
            rank[b[(a[i]/base)%10]--]=a[i];
        for(int i=1;i<=n;i++) a[i]=rank[i];
    }
    inline void sort(){
        int max=0;
        for(int i=1;i<=n;i++)
            if(a[i]>max) max=a[i]; 
        while(max/base){
            Qsort();
            base=(base<<1)+(base<<3);
        }
    }
    int main(){
        read(n);
        for(int i=1;i<=n;++i) read(a[i]);
        sort();
        for(int i=1;i<=n;++i) printf("%d ",a[i]);
    }
    /*
    8
    54 23 674 12 544 32 9 142
    */
    
  • 相关阅读:
    Fedora 23 配置
    小小的告别一下这个博客
    markdown测试
    ihhh题解
    【BZOJ】1998: [Hnoi2010]Fsk物品调度
    【BZOJ】2563: 阿狸和桃子的游戏
    【BZOJ】3712: [PA2014]Fiolki
    【BZOJ】2333: [SCOI2011]棘手的操作
    我的vimrc
    Ubuntu Gnome 14.04.2 lts 折腾笔记
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/11007481.html
Copyright © 2020-2023  润新知