文章目录
- 基数排序定义
- 基数排序算法
- 基数排序算法分析
基数排序定义
前述的各类排序方法都是建立在关键字啊比较的基础上,而分配类排序不需要比较关键字的大小,它是根据关键字中各位的值,通过对待排序记录进行若干趟分配与收集来实现排序的,是一种借助于多关键字排序的思想对单关键字排序的方法。基数排序是典型的分配类排序。
-  基数排序也叫桶排序或箱排序: 
-  基本思想:分配 + 收集。 - 分配:设置若干个箱子,将关键字为 k 的记录放入第 k 个箱子;
- 收集:然后在按序号将这第 k 个箱子里的内容再拿出来链接在一起。
 
-  基数排序:数字是有范围的,均由 0 - 9 这是个数字组成,则只需要设置十个箱子,相继按照个、十、百…进行排序。 
举个例子
现在这样一组数据进行排序(614,738,921,485,637,101,215,530,790,306)
- 这一堆数据的每一位数都是由 0-9 这十个数字构成的。
- 准备 0-9 这十个箱子,把这些数扔到这些箱子里去。
- 第一趟分配:按个位排,个位是多少就扔到哪个箱子里,如:614 就扔到 4 号箱子去。 
  - 分配完成之后,将个位数按照 0-9 的顺序将这些数据收集回来。
- 收集回来之后,这些数据的个位就有序了。
 
- 第二趟分配:按十位排 
  - 分配完成之后,再将十位按照从 0-9 的顺序把它们收集回来。
 
- 第三趟收集:按百位排 
  - 分配完之后再来一次收集,此时所有的数据已经有序递增了。
 
基数排序口诀
- 三分三收,个十百
基数排序算法
算法实现时采用静态链表, 以便于更有效的存储和重排记录。
相关数据类型的定义:
#define MAXNUM_KEY 8 //关键字项的最大值
#define RADIX 10	//关键字基数,此时是十进制整数的基数
#define MAX_SPACE 10000
typedef struct
{
		KeyType keys[MAXNUM_KEY];//关键字
		InfoType otheritems;	//其他数据项
		int next;
}SLCell;	//静态链表的结点类型
typedef struct
{
		SLCell r[MAX_SPACE];//静态链表的可利用空间,以r[0]为头结点
		int keynum;	//记录当前的关键字个数
		int rcnum;	//静态链表的当前长度
}SLList;	//静态链表类型
typedef int ArrType[RADIX];	//数组类型
基数排序算法描述
- 分配算法
//静态链表L的r域中记录已按照(keys[0],...,key[i-1])有序
//本算法按照第i个关键字keys[i]建立RADIX个子表,使同一个子表中记录的keys[i]相同
//f[0...RADIX-1]和e[0...RADIX-1]分别指向各子表中第一个和最后一个元素
void Distribute(SLCell &r,int i,ArrType &f,ArrType &e)
{
		for(j = 0;j < RADIX;++j)
		{
				f[j] = 0;//将各子表初始化为空表
		}
		for(p = r[0].next;p;p = r[p].next)
		{
				j = ord(r[p].keys[i]);	//ord将记录第i个关键字映射到[0...RADIX-1]
				if(!f[j])
				{
						f[j] = p;
				}
				else
				{
						r[e[j]].next = p;
				}
				e[j] = p;	//将p所指向的结点插入第i个子表中
		}
}
- 收集算法
//本算法按照keys[i]自小直大将f[0...RADIX-1]所指向的各子表依次连接成一个链表
//e[0...RADIX-1]为各个子表的尾指针
void Collect(SLCell &r,int i,ArrType f,ArrType e)
{
		for(j = 0;!f[j];j = succ(j));	//找第一个非空子表,succ为求后继函数
		r[0],next = f[j];t = e[j];		//r[0].next指向第一个非空子表中的第一个结点
		while(j < RADIX)
		{
				for(j = succ(j);j < RADIX-1 && !f[j];j = succ(j));//找下一个非空子表
				if(f[j])
				{
						//链接两个非空子表
						r[t].next = f[j];
						t = e[j];
				}
		}
		r[t].next = 0;	//让t指向最后一个非空子表中的最后一个结点
}
- 基数排序
//对L采用静态链表表示的顺序表
//对L左基数排序,使得L成为按关键字自小到大的有序静态林彪,以L.r[0]为头结点
void RadixSort(SLList &L)
{
		for(i = 0;i < L.recnum;++i)
		{
				L.r[i].next = i+1;
		}
		L.r[L.recnum].next = 0;	//将L改造为静态链表
		for(i = 0;i < L.keynum;++i)	//按照最低位优先依次对各关键字进行分配和收集
		{
				Distribute(L.r,o.f.e);//第i趟分配
				Collect(L.r,i,f,e);//第i趟收集
		}
}
基数排序算法分析
时间复杂度
- 时间效率:O(k * (n + m)) 
  - k:关键字个数(决定分配多少趟)
- n:元素个数(每一趟需要分配多少个元素)。
- m:关键字取值范围为 m 个值(桶的个数/进行收集的次数)。
 
- 分配:基数排序需要进行元素分配。 
  - 分配过程总共要做的次数:看关键字的个数,有 个、十、百 三个关键字就需要分配三次。
 
- 收集:要将每个桶内的数据收集回来。 
  - 收集的次数同样也是看关键字的个数。
 
空间复杂度
- 空间效率:O(n + m)
- 分配的时候要分配到 m 个桶里,收集回来的时候需要放在长度为 n 的数组中。
算法特点
- 是稳定排序。
- 可用以链式结构,也可用于顺序结构。
- 世界复杂度可以突破基于关键字比较一类方法的下界,O(nlog₂n),达到 O(n)。
- 技术排序使用条件有严格的要求:需要知道各级关键字的主次关系和各级关键字的取值范围。
下一节传送门:各排序方法比较























