基数排序 学习笔记
基数排序
一种非比较的排序算法。我们把每个元素拆成 \(k\) 个关键字。然后有两种排序方式:
- MSD,从第 \(1\) 关键字到第 \(k\) 关键字依次排序。
- LSD,从第 \(k\) 关键字到第 \(1\) 关键字一次排序。
这里对于两个元素 \(a,b\) 判断 \(a<b\) 的方法是,找到最小的关键字使得 \(a,b\) 在这个关键字上的值不同,并且 \(v_a<v_b\),\(v\) 为这个关键字上的值。
MSD
枚举当前关键字,然后对当前关键字排序,按值分组,递归每组并进行下一关键字的排序。
此时基数排序的稳定性与对一个关键字排序的稳定性有关。
个人认为 MSD 并没有 LSD 常用。
LSD
从大往小枚举关键字,然后对全局进行当前关键字的稳定排序,最后的元素就是有序的了。
此时基数排序是稳定排序。
对一个关键字的排序方法可以采用计数排序,即开一个大小为当前关键字值域的数组,数组的每个位置存一个 vector 表示这一关键字相同值的元素。
计数排序也可以对值域的每个前缀求有多少个数,然后从大到小填进去,每次把 \(v\) 填进 \(cnt_v\),然后令 \(cnt_v\) 减一。
时间复杂度
记 \(w_i\) 为第 \(i\) 个关键字的值域,\(n\) 为元素个数,那么 MSD 和 LSD 的复杂度都是 \(O(nk+\sum w_i)\)。
实现
const int N=1e5+5;
int n;
int a[N];
vi b[100];
signed main(){
read(n);
fo(i,1,n) {
read(a[i]);
}
int t=100;
for(int i=1;i<=1e9;i*=t) {
fu(j,0,t) b[j].clear();
fo(j,1,n) b[a[j]/i%t].pb(a[j]);
int tot=0;
fu(j,0,t) for(int k:b[j]) a[++tot]=k;
}
fo(i,1,n) write(a[i],' ');
return 0;
}
另一种实现。
const int N=1e5+5;
const int T=100;
int n;
int a[N],b[N];
int c[T+5];
signed main(){
read(n);
fo(i,1,n) {
read(a[i]);
}
for(ll t=1;t<=1e9;t*=T) {
memcpy(b,a,sizeof a);
fo(i,0,T) c[i]=0;
fo(i,1,n) c[a[i]/t%T]++;
fo(i,0,T) c[i+1]+=c[i];
fd(i,n,1) a[c[b[i]/t%T]--]=b[i];
}
fo(i,1,n) write(a[i],' ');
return 0;
}

浙公网安备 33010602011771号