说在前面
基数排序是一种高效的排序算法,多用于处理字符串(不支持负数,实数排序效率不高)
算法流程
· 基数排序就是以每一位的数值为关键字来排序,也就是说,是按位排序(一般从低位向高位枚举)
先贴代码
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
然后到了
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
*/
QWQ