基数排序 学习笔记

基数排序

一种非比较的排序算法。我们把每个元素拆成 \(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)\)

实现

P1177【模板】排序

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;
}
posted @ 2025-04-14 22:13  dengchengyu  阅读(20)  评论(0)    收藏  举报