基数排序

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

算法流程

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

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
*/
posted @ 2019-06-12 09:47  Kreap  阅读(1814)  评论(0编辑  收藏  举报