說在前面
基數排序是一種高效的排序算法,多用於處理字符串(不支持負數,實數排序效率不高)
算法流程
· 基數排序就是以每一位的數值為關鍵字來排序,也就是說,是按位排序(一般從低位向高位枚舉)
先貼代碼
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] ,則滿足升序,最終順序正確(實在不懂得小伙伴可以邊啃代碼邊模擬一下,自認為講的很清楚了QwQ)
大概就這樣了吧,貼上完整代碼:
#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
*/