一.相关概念
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort.它是桶排序的扩展,其中基数指得是一个关键字取值的种数,例如十进制的整数,每位的数字都可以取0~9中的一个数,有10种取法,所以十进制整数的基数是10
基数排序的基本思想是:将整数按位切割成不同的数字,然后将每位的数字进行排序; 具体做法是将所有待排序数值统一为相同数位长度,数位较短的数前面补零。然后从最低位开始,依次进行一次分配排序,以每位的数字为序号分配到对应的序号的队列,全部数字分配完成后,再进行出队收集(从低序号队列开始收集).然后再以同样的原理分配和收集下一位,直到最高位也完成分配和收集,则排序完成
该算法一共需要分配收集d趟,d为序列中最大的那个数字的位数,如果最大的数字有3位,则需分配和收集3趟,如果有n位,则需要分配收集n趟
如图:
二.思路分析
这里来讲一讲代码中怎么实现基数排序
1.首先,我们知道基数排序分配和收集需要d趟,而这个d取决于序列中的最大的那个数字位数.所以我们用选择法先对序列进行一次遍历,选出最大的那个数字,然后我们对那个数字取10的对数,就得到了数字有多少位,也就是算出了d
2.为了实现分配和收集,我们可以创建一个容量为10的数组来存储10个队列(队头指针),数组的索引就是队列的序号,分配时以每位的数字为序号,分配到对应序号的队列(入队),收集时按队列从小到大的序号顺序进行出队收集
3.怎么切割一个数字的每位数字?这个也容易,假如我们要分离数字X的个位,我们只需使X除以1取整然后模以10就能得到个位数字,即(X/1)%10,要分离十位,我们就使X除以10取整然后模以10就能得到十位数字,即(X/10)%10;同理,百位就是(X/100)%10,千位就是(X/1000)%10,万位,百万位,千万位…也是同样的原理
好了,知道了上面几个知识点我们就可以写代码了
三.代码实现
注意:顺序表中零单元不用,从索引1开始存储元素
//基数排序(排列为递增序列)
void RadixSort(SqList &L)
{
int max=L.elem[1];
for(int i=1;i<=L.length;i++) //找出最大元素
max=max>L.elem[i]?max:L.elem[i];
int d=log(max)/log(10)+1; //十进制元素的位数
typedef struct Node //链结点
{
ElemType e;
struct Node *next;
}*LinkList,LNode;
LinkList front[10]; //10个队头指针,构建10个链队列
LinkList rear[10]; //10个队尾指针
for(int i=0;i<=9;i++) //给队头申请内存
{
front[i]=(LinkList)malloc(sizeof(LNode));
front[i]->next=NULL;
}
int i,n; //i线性表遍历位置,n队列序号
LinkList node; //链结点指针
for(int count=1,x=1;count<=d;count++,x*=10) //count排序趟数,x除数
{
for(n=0;n<=9;n++) //初始队尾指针指向队头
rear[n]=front[n];
for(i=1;i<=L.length;i++) //分配
{
n=(L.elem[i]/x)%10; //数字其中一位的数
node=(LinkList)malloc(sizeof(LNode)); //创建新结点
node->e=L.elem[i]; //存入数据
node->next=NULL;
rear[n]->next=node; //入队(后接法),分配到对应序号的队列
rear[n]=node;
}
for(n=0,i=1;n<=9;n++) //收集
{
for(node=front[n]->next;node!=NULL;node=front[n]->next,i++) //依次把0~9号队列的元素收集起来
{
L.elem[i]=node->e; //读取数据
front[n]->next=node->next; //出队
free(node); //删除出队结点
}
}
}
}
操作结果: