数据结构的基数排序因为各种结构的相互交叉繁琐等等,往往让我们新手很难理解得通透。所以我就拿我们学校OJ上的一道基数排序题来剖析一下基数排序要怎么做。
由题可知我们是要用最低优先法,简称LSD(还有一种是最高优先法,MSD),最低优先法,顾名思义,即从最低位对它进行排序,看输出样例那里,最低位由0-3-3-4-5-8-9-9-9排序起来,而接下来的输出就应该不用我说了吧。
下面开始对代码进行分析。
#define MAX_NUM_OF_KEY 8
#define RADIX 10
#define MAX_SPACE 10000
typedef int Keystype;
typedef struct
{
Keystype keys[MAX_NUM_OF_KEY];//关键字
int next;//这个用于下一项
}SLCell;//静态链表的结点类型
typedef struct
{
SLCell r[MAX_SPACE];//静态的链表的存储空间
int keynum;//记录当前的关键字个数
int recnum;//静态链表当前长度
}SLList;//静态链表类型
typedef int ArrType[RADIX];//指针数组类型
我将使用上面的结构,建立SLList L和ArryType f,e
下面看主程序
int main()
{
int i,j,t;
SLList L;//建立链表
L.keynum=0;
scanf("%d",&L.recnum);
for(i=1;i<=L.recnum;i++)
{
for(j=0;j<8;j++)
L.r[i].keys[j]=0;//这里是指对关键字的每一位数进行初始化
scanf("%d",&t);
for(j=0;t&&j<8;t=t/10,j++)
L.r[i].keys[j]=t%10;//对关键字进行赋值
if(j>L.keynum)
L.keynum=j;//最大位数的关键字
}
RadixSort(L);//调用函数
return 0;
}
对于主程序理解起来应该没什么问题,下面看RadixSort函数
void RadixSort(SLList &L)
{
int i;
ArrType f,e;
for(i=0;i<L.recnum;i++)
L.r[i].next=i+1;//相当于把每个关键字的下标赋值到next,这样就可以连接起来访问
L.r[L.recnum].next=0;//把最后一个下一个指向0
for(i=0;i<L.keynum;i++)//这里是对每一位数整理后遍历一次
{
Distribute(L,i,f,e);//i是指将要进行排序的位数(如:个位)
Collect(L,i,f,e);
print_SLList(L);
}
}
void print_SLList(SLList L)//这部分是输出
{
int i,k;
for(i=L.r[0].next;i!=0;i=L.r[i].next)
{
for(k=L.keynum-1;k>=0;k--)
printf("%d",L.r[i].keys[k]);
printf(" ");
}
printf("\n");
}
写到这里可能会有些人对next这个设定有点疑问,这个是与链表的有点像,但它的类型是int,所以这里应该要把它当作数组的下标来看,如:一个数组r[]=3,7,2,8,1; next=1; r[next]=7;
而L.r[i].next=i+1;是指L.r[i]的后面带一个小尾巴,通过它指向下一数,从而把所有的数连起来。
下面是Distribute函数
void Distribute(SLList &L,int i,ArrType f,ArrType e)
{
int j,p;
for(j=0;j<RADIX;j++)
f[j]=0;//对f[]数组进行初始化
for(p=L.r[0].next;p;p=L.r[p].next)
{
j=L.r[p].keys[i];//j是下标为p的关键字的某位数的值
if(!f[j])
f[j]=p;//对下标进行放置(f[j]是相同组j的第一个)
else
L.r[e[j]].next=p;//出现重复j的话,则把它的下标赋值给next
e[j]=p;//对下标进行放置(它是相同数的最后一个)
//也就说j相同,输出f[j] -> next ->next ->...->e[j],注意p是下标
}
}
关于上面的数组f[]和e[],我们不妨可以换一种思考方式,把f[i]与e[i]当作是同一组的,如e[0],f[0]是同一组的,e[2],f[2]是同一组的,它们不同的是,f[]是这个组的第一位,e[]是最后一位,而中间是位数(如:个位)相同的各个数。而这些功能由for(p=L.r[0].next;p;p=L.r[p].next)这个循环实现,但是该函数完成后,整条链表会被分成f[0]…f[9]由f[]开头的十个组。
接下来由Collect函数把这十个组连起来
void Collect(SLList &L,int i,ArrType f,ArrType e)
{
int j,t;
for(j=0;!f[j];j++);//找到第一个存有数的那一组
L.r[0].next=f[j];//把第一个给赋值
t=e[j];//把对应的下标给t
while(j<RADIX-1)
{
for(j=j+1;j<RADIX-1&&!f[j];j++);
if(f[j])
{
L.r[t].next=f[j];//把下标给再给下一个
t=e[j];//把每一组衔接起来
}
}
L.r[t].next=0;
}
上面的代码是把上一组的末尾的next接上下一组的头,例如:上一组是f[0]…e[0],下一组是f[1]…e[1],也就是把e[0]的next赋值f[1],这样就成功做到连接起来。
以上就是按照我对基数排序的理解啦,希望大家看到之后能够学到点东西。我是Dream致远,一位正在成长的程序猿,如果有什么错误的欢迎大家指出。谢谢。