基数排序的原理与实现

基数排序是一种非比较型整数排序算法,利用计数排序或桶排序的思想,按位切割分别比较。文章详细介绍了基数排序的原理,包括如何进行多轮排序,并通过C++和Java版本的实现代码展示其实现细节。基数排序适用于数值范围有限且需要高效排序的场景,弥补了计数排序和桶排序的局限性。文章最后比较了基数排序与JDK的TimSort在不同数据量下的性能,并提出基数排序在特定情况下的优势。
摘要由CSDN通过智能技术生成

一、前言

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。

比较型排序:常见的快速排序,归并排序,冒泡排序……等等,都是基于比较的排序算法。
比较型排序算法时间复杂度下界为O(N*log2N) ,
而非比较型排序算法有:计数排序,桶排序,基数排序等;
其中,计数排序,桶排序的时间复杂度分别为O(n+m)和O(n),线性的时间复杂度。

计数排序和桶排序这么快,为什么STL, JDK等没有采用呢?
因为适用场景比较窄。
要使用这两个算法,需满足如下条件:
1、排序项是数值:这个就很伤了,比如不能用来排序字符串了,更不要说各种复杂对象了;
2、范围比较小:如果数值范围比较大,需要的计数器或者桶就会很多,空间复杂度上无法承受。

比如阿里面试题有一道是给2万名员工按年龄排序,就可以用计数排序或桶排序了,
因为年龄是整数,而且范围小,比如用桶排序,顶多100个桶就够了。

而基数排序,正是解决计数排序和桶排序数值范围局限性的良方。

二、原理

基数排序是这样实现的:
将所有待比较数值统一为同样的数字长度,数字较短的数前面补零。
然后,从最低位开始,依次进行一次排序。
这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

以上是以10为“基数”的过程演示。
整个过程经过三轮排序,先个位,再十位,然后是百位; 三轮排序完成后,整个数列就有序了。
那这三轮排序都是怎么操作的呢?计数排序或桶排序都可以。
所以基数排序和计数排序、桶排序的关系是一种拓展的关系。
其背后时一种时间和空间的交换。
比如说这里如果用1000个桶,那么只用一轮排序就好了(这就退化到计数排序了)。
这里先不讨论是否划算(用10为基通常是不划算的), 先分析思想原理。

如果说基数排序的核心奥义是“按位切割,分别比较”的话,那么
计数排序就是:先统计,后索引;
桶排序则是:先分配,后收集。
什么时候用计数排序,什么时候用基数排序?
我的理解是,数组用计数排序,链表则用桶排序(收集的时候执行各个桶的元素首位相接即可)。

下面是整个基数排序的过程(用上面的数据为例):

第一轮:
0: 170, 90
1:
2: 802, 2
3:
4: 24
5: 45,75
6: 66
7:
8:
9:

第二轮:
0: 802, 2
1:
2: 24
3:
4: 45
5:
6: 66
7: 170, 75
8:
9: 90

第三轮:
0: 2, 24, 45, 66, 75, 90
1: 170
2:
3:
4:
5:
6:
7:
8: 802
9:

最终,顺序为: 2, 24, 45, 66, 75, 90,170, 802
值得一提的是,实现这个过程的一个必要条件是:计数排序和桶排序是稳定排序

三、实现

C++版本

template <class T>
void countSort(T *src, T *des, int n, int shift) {
   
	// 初始化统计变量为0
	int cnt[256] = {
    0 };

	// 获取元素的value,通过位移和掩码获取[shift,shift+8]的bit, 索引到对应的统计变量(cnt)
	// 统计散落到各cnt的元素的数量
	for (int i = 0; i < n; i++) {
   
		T value = src[i];
		cnt[(value >> shift) & 0xFF]++;
	}

	// 根据cnt统计的元素个数,计算每个一组元素的末端位置
	for (int i = 1; i < 256; i++) {
   
		cnt[i] += cnt[i - 1];
	}

	// 再次根据元素的value索引到对应的统计变量(cnt)
	// 结合上一步前面计算的位置,将各个元素放到对应的位置(另一个素组)
	for (int i = n - 1; i >= 0; i--) {
   
		T value = src[i];
		des[--cnt[(value >> shift) & 0xFF]] = src[i];
	}
}

template <class T>
void rsort(T *src, int n) {
   
	T* des = new T[n];
	int size = sizeof(T);

	// 以256为基,则需要每次取元素的8bit进行计数排序,从低位到高位
	// bit为一个字节,则元素大小有多少个字节就需要进行多少次计数排序
	for (int i = 0; i < size; i++) {
   
		if (i % 2 == 0)
			countSort(src, des, n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值