基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。
一般情况下,假设有n个记录的序列
最高位优先法(MSD法):先对最主位关键字 K0 进行排序,按 K1 值不同再分成若干更小的子序列,依次重复,直至对 Kd−2 进行排序后得到的每一子序列中的记录都具有相同的关键字 (K0i,K1i,…,Kd−2i) ,而后分别每个子序列对 Kd−1 进行排序,最后将所有子序列一次联接在一起称为一个有序序列。
最低位优先法(LSD法):从最次位关键字 Kd−1 起进行排序。然后再对高一位的关键字 Kd−2 进行排序,一次重复,直至对 K0 进行排序后便成为一个有序序列。
MSD和LSD只约定按什么样的“关键字次序”来进行排序,而未规定对每个关键字进行排序时所用的方法。两种排序方法的不同特点:若按MSD进行排序,必须将序列逐层分割成若干子序列,然后对各子序列参加排序;而按LSD进行排序时,不必分成子序列,对每个关键字都是整个序列参加排序,但对 Ki(0≤i≤d−2) 进行排序时,只能用稳定的排序方法。另一方面, 按LSD进行排序时,在一定的条件下(即对前一个关键字 Ki(0≤i≤d−2) 的不同值,后一个关键字 Ki+1 均取相同值),也可以通过若干次“分配”和“收集”来实现排序。通常将对不同元素的分类称为分配,排序的过程称为收集。
基本算法思想:假设第i个元素 ai 的关键字 keyi , keyi 是由d位十进制组成,即 keyi=kdikd−1i…k1i ,其中 k1i 为最低位, kdi 为最高位,关键字的每一位数字都可作为一个关键字。首先将元素序列按照最低的关键字进行排序,然后从低位到高位直到最高位依次进行排序,这样就完成了排序的过程。
例如对于一组元素的关键字序列为(236,128,34,567,321,793,317,106),这组关键字位数最多的是3位,在排序之前,首先将所有元素都转换为3位数字组成的数,即(236,128,034,567,321,793,317,106),对这组元素进行基数排序需要进行3趟分配和收集,首先需要对元素序列的关键字的最低位即个位上的数字进行分配和收集,然后对十位上的数字进行分配和收集,最后是对最高位的数字进行分配和收集。一般情况下, 采用链表实现基数排序。
由以上很容易看出,经过第1趟排序即对个位数字作为关键字进行分配后,关键字被分为10类,个位数字相同的数字被划分为一类,对分配后的元素进行收集之后,得到以个位数字非递减排列的元素。同理,经过第2趟分配和收集后,得到以十位数字非递减排列的元素。经过第3趟分配和收集后,得到最终的排序结果。
- 类型定义头文件
#define MAX_NUM_OF_KEY 8 /* 关键字项数的最大值 */
#define RADIX 10 /* 关键字基数,此时是十进制整数的基数 */
#define MAX_SPACE 1000
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义RedType类型的关键字为整型 */
typedef struct
{
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项 */
}RedType; /* 记录类型*/
typedef char KeysType; /* 定义关键字类型为字符型 */
typedef struct
{
KeysType keys[MAX_NUM_OF_KEY]; /* 关键字 */
InfoType otheritems; /* 其它数据项 */
int next;
}SLCell; /* 静态链表的结点类型 */
typedef struct
{
SLCell r[MAX_SPACE]; /* 静态链表的可利用空间,r[0]为头结点 */
int keynum; /* 记录的当前关键字个数 */
int recnum; /* 静态链表的当前长度 */
}SLList; /* 静态链表类型 */
typedef int ArrType[RADIX]; /* 指针数组类型 */
- 函数文件
void InitList(SLList *L,RedType D[],int n)
{ /* 初始化静态链表L(把数组D中的数据存于L中) */
char c[MAX_NUM_OF_KEY],c1[MAX_NUM_OF_KEY];
int i,j,max=D[0].key; /* max为关键字的最大值 */
for(i=1;i<n;i++)
if(max<D[i].key)
max=D[i].key;
(*L).keynum=(int)(ceil(log10(max)));
(*L).recnum=n;
for(i=1;i<=n;i++)
{
(*L).r[i].otheritems=D[i-1].otherinfo;
itoa(D[i-1].key,c,10); /* 将10进制整型转化为字符型,存入c */
for(j=strlen(c);j<(*L).keynum;j++) /* 若c的长度<max的位数,在c前补'0' */
{
strcpy(c1,"0");
strcat(c1,c);
strcpy(c,c1);
}
for(j=0;j<(*L).keynum;j++)
(*L).r[i].keys[j]=c[(*L).keynum-1-j];
}
}
int ord(char c)
{ /* 返回k的映射(个位整数) */
return c-'0';
}
void Distribute(SLCell r[],int i,ArrType f,ArrType e)
{ /* 静态键表L的r域中记录已按(keys[0],...,keys[i-1])有序。*/
/* 按第i个关键字keys[i]建立RADIX个子表,使同一子表中记录的keys[i]相同。 */
/* f[0..RADIX-1]和e[0..RADIX-1]分别指向各子表中第一个和最后一个记录 */
int j,p;
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所指的结点插入第j个子表中 */
}
}
int succ(int i)
{ /* 求后继函数 */
return ++i;
}
void Collect(SLCell r[],ArrType f,ArrType e)
{ /* 按keys[i]自小至大地将f[0..RADIX-1]所指各子表依次链接成 */
/* 一个链表,e[0..RADIX-1]为各子表的尾指针。 */
int j,t;
for(j=0;!f[j];j=succ(j)); /* 找第一个非空子表,succ为求后继函数 */
r[0].next=f[j];
t=e[j]; /* r[0].next指向第一个非空子表中第一个结点 */
while(j<RADIX-1)
{
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指向最后一个非空子表中的最后一个结点 */
}
void RadixSort(SLList *L)
{ /* L是采用静态链表表示的顺序表。对L作基数排序,使得L成为按关键字 */
/* 自小到大的有序静态链表,L.r[0]为头结点。 */
int i;
ArrType f,e;
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,i,f,e); /* 第i趟分配 */
Collect((*L).r,f,e); /* 第i趟收集 */
printf("第%d趟收集后:\n",i+1);
printl(*L);
printf("\n");
}
}
void Sort(SLList L,int adr[]) /* 改此句(类型) */
{ /* 求得adr[1..L.length],adr[i]为静态链表L的第i个最小记录的序号 */
int i=1,p=L.r[0].next;
while(p)
{
adr[i++]=p;
p=L.r[p].next;
}
}
void Rearrange(SLList *L,int adr[]) /* 改此句(类型) */
{ /* adr给出静态链表L的有序次序,即L.r[adr[i]]是第i小的记录。 */
/* 本算法按adr重排L.r,使其有序。算法10.18(L的类型有变) */
int i,j,k;
for(i=1;i<(*L).recnum;++i) /* 改此句(类型) */
if(adr[i]!=i)
{
j=i;
(*L).r[0]=(*L).r[i]; /* 暂存记录(*L).r[i] */
while(adr[j]!=i)
{ /* 调整(*L).r[adr[j]]的记录到位直到adr[j]=i为止 */
k=adr[j];
(*L).r[j]=(*L).r[k];
adr[j]=j;
j=k; /* 记录按序到位 */
}
(*L).r[j]=(*L).r[0];
adr[j]=j;
}
}
- 主程序
#define N 10
void print(SLList L)
{ /* 按数组序号输出静态链表 */
int i,j;
printf("keynum=%d recnum=%d\n",L.keynum,L.recnum);
for(i=1;i<=L.recnum;i++)
{
printf("keys=");
for(j=L.keynum-1;j>=0;j--)
printf("%c",L.r[i].keys[j]);
printf(" otheritems=%d next=%d\n",L.r[i].otheritems,L.r[i].next);
}
}
void printl(SLList L)
{ /* 按链表输出静态链表 */
int i=L.r[0].next,j;
while(i)
{
for(j=L.keynum-1;j>=0;j--)
printf("%c",L.r[i].keys[j]);
printf(" ");
i=L.r[i].next;
}
}
void main()
{
RedType d[N]={{278,1},{109,2},{63,3},{930,4},{589,5},{184,6},{505,7},{269,8},{8,9},{83,10}};
SLList l;
int *adr;
InitList(&l,d,N);
printf("排序前(next域还没赋值):\n");
print(l);
RadixSort(&l);
printf("排序后(静态链表):\n");
print(l);
adr=(int*)malloc((l.recnum)*sizeof(int));
Sort(l,adr);
Rearrange(&l,adr);
printf("排序后(重排记录):\n");
print(l);
}
- 测试结果