数据结构之基数排序

数据结构的基数排序因为各种结构的相互交叉繁琐等等,往往让我们新手很难理解得通透。所以我就拿我们学校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致远,一位正在成长的程序猿,如果有什么错误的欢迎大家指出。谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值