基数排序

介绍基数排序算法原理及使用链表实现的过程,包括分发和收集步骤,通过具体实例展示如何对数字进行排序并去除重复项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

描述1:基数排序是一种多关键字排序方法,它不需要比较两个数的大小就可以实现排序。排序的基本思想是将每个数进行分解,比如一个三位数123可以分解成三个关键字,分别是个位3,十位2,百位1,然后按照关键字分别进行“分发”和“收集”过程。

描述2:“分发”的过程是把需要排序的数字按照对应的关键字分发到相应的集合中,比如对于三个数123,920,156,如果按照“个位关键字”进行分发,那么123分发到集合“3”中,920分发到集合“0”中,156分发到集合“6”中;如果按照“十位关键字”进行分发,那么123分发到集合“2”中,920分发到集合“2”中,排在123的后面,156分发到集合“5”中。

描述3:“收集”的过程是把分发的数字,按照所归属集合序号的大小重新进行排列。比如123,920,156按照“个位关键字”分发后,分别归属于三个集合“3”,“0”和“6”,如果按照集合的序号进行升序收集,那么收集过后的三个数字重新排列为920,123,156。

描述4:基数排序的主要思想就是按照关键字从“低位到高位”的顺序,不断地进行“分发”和“收集”。假设对一组三位数进行升序排序,则总共需要三轮过程:

原始状态:123,920,156,723,850

第1轮分发(个位关键字):

集合0={920,850};集合1={};集合2={};集合3={123,723};集合4={};

集合5={};集合6={156};集合7={};集合8={};集合9={}。

第1轮收集:920,850,123,723,156

第2轮分发(十位关键字):

集合0={};集合1={};集合2={920,123,723};集合3={};集合4={};

集合5={850,156};集合6={};集合7={};集合8={};集合9={}。

              第2轮收集:920,123,723,850,156

              第3轮分发(百位关键字):

集合0={};集合1={123,156};集合2={};集合3={};集合4={};

集合5={};集合6={};集合7={723};集合8={850};集合9={920}。

              第3轮收集:123,156,723,850,920

描述5:按照以上思路,对于分发过程中的每个“集合”,可以构建一个单向链表,链表中的每个节点存储分发给该集合的数字,图1表示的是第1轮分发的结果。

图1 第1轮按照“个位关键字”的分发结果

 

单向链表中每个节点称为“数字节点”,定义如下:

typedef struct numNode // 数字节点定义

{

       charnum[4];  // 存储分发给集合的数字,这里规定数字都是“三位数”,其中

num[0]~num[2]分别存储百位数、十位数和个位数,num[3]为字符串结束符。

       structnumNode *next;  // 指向下一个数据节点的指针

} NUMNODE;

 

在图1中,每个单向链表的“头指针”和“尾指针”分别组成了一个一维指针数组,定义如下:

NUMNODE *h[10], *t[10];

其中,h和t的“下标”代表分发集合的序号,范围为0~9。

 

描述6:根据上面的链表结构,“收集”的过程就是从序号0开始,顺序遍历头指针数组h,将“非空”的单向链表头尾相连成一个新的链表,如图1的三个单向链表“头尾相连”形成一个新的链表,

此后,再将这个单向链表重新分发到集合中,进行第二轮过程。

 

 

描述7:提供main函数如下。

main()

{

       NUMNODE*h[10], *t[10];  // 存储分发集合的头指针数组和尾指针数组

       NUMNODE*head;  // 存储需要排序数据的单向链表头指针

       char *string= "123,987,003,632,123";  // 需要排序的数据

       int i;

      

       head =createNumList(string);  /* 根据初始字符串,将排序数据存放在单向链表中,本例子形成的单向链表为*/

       for(i=2;i>=0;i--)    // 从“个位关键字”开始,进行反复的分发和收集

       {

              distribute(head, h, t, i);  //将单向链表中的数字节点分发到集合中

              head = collect(h, t); //将集合中的数字节点收集为一个新的单向链表

       }

      

       head =delRepeatedNum(head);  //删除“升序排列”链表的重复数字节点

       printNumList(head);   //打印链表

}

具体代码:

//Author ChenXingman  http://www.youkuaiyun.com/Carp_and_Wind  2013.3.3

#include <stdio.h>
#include <string.h>
#include <malloc.h>
typedef struct numNode        // 数字节点定义
{
char num[4];             // 存储分发给集合的数字,这里规定数字都是"三位数",其中
                            // num[0]~num[2]分别存储百位数、十位数和个位数,num[3]为字符串结束符。
struct numNode *next;  // 指向下一个数据节点的指针
} NUMNODE;
void printNumList(NUMNODE *head)
{
NUMNODE *p1,*p2;
p1=p2=head;
while(p1!=NULL)
{
p2=p1;
printf("%s\n",p1->num);
p1=p2->next;
}
}
NUMNODE *createNumList(char string[])
{
NUMNODE *head,*p1,*p2;
head=(NUMNODE *)malloc(sizeof(NUMNODE));
head->num[0]='1';
head->num[1]='2';
head->num[2]='3';
head->num[3]='\0';
p1=p2=head;
for(int i=4;i<strlen(string);i=i+4)
{
p2=p1;
p1=(NUMNODE *)malloc(sizeof(NUMNODE));
p1->num[0]=string[i];
p1->num[1]=string[i+1];
p1->num[2]=string[i+2];
p1->num[3]='\0';
p2->next=p1;
}
p1->next=NULL;
return head;
}
void distribute(NUMNODE* head, NUMNODE** h, NUMNODE** t, int key)
/*功能:将单向链表中的数字节点分发到集合中。其中head为单向链表头指针;h和t分别为
集合(0~9)的头指针数组和尾指针数组(参考图1),即数组的下标范围从0~9;key表示按照
第几位关键字进行分发。假定head肯定不为NULL*/
{
for(int i=0;i<10;i++)
{
t[i]=NULL; 
h[i]=NULL;
}//初始化 
NUMNODE *p1,*p2,*temp;
p1=p2=head;
while(p1!=NULL)  //遍历 
{
p2=p1;
        //printf("%s\n",p1->num);
temp=(NUMNODE *)malloc(sizeof(NUMNODE));
int count=p1->num[key]-'0';         //p1->num[key]复制 
for(int i=0;i<=3;i++)
{temp->num[i]=p1->num[i];}
temp->next=NULL;       //copy
        //printf("%s\n",temp->num);
        //printf("count=%d\n",count);                  
if(t[count]==NULL&&h[count]==NULL)//插入: 
{
h[count]=temp;
t[count]=temp;
}
else
{
t[count]->next=temp;
t[count]=temp;
}
p1=p2->next;
}
/*for(int i=0;i<10;i++)
{
printf("t--%s\n",t[i]->num);
}*/
}
NUMNODE* collect(NUMNODE** h, NUMNODE** t)
/* 功能:将集合(0~9)中每个"非空"单向链表"首尾相连"收集成一个新的单向链表。其中
h和t分别为集合(0~9)的头指针数组和尾指针数组*/
{
NUMNODE *head=NULL;
NUMNODE *p1;//p1为尾指针 
p1=head;
/*for(int i=0;i<10;i++)
{
printf("t--%s\n",t[i]->num);
}*/
for(int i=0;i<10;i++)
{
if(h[i]!=NULL&&t[i]!=NULL)
{
if(head==NULL)
{
head=h[i];
p1=t[i];
//printf("tou:%s wei:%s\n",head->num,p1->num);
}
else
{
//printf("shangyigewei:%s\nxiayigetou:%s\n",p1->num,h[i]->num);
p1->next=h[i];
p1=t[i];
   //printf("xianzaidewei:%s\n",p1->num);
}
}
}
return head;
}
NUMNODE* delRepeatedNum(NUMNODE* head)
/*功能:删除"升序排列"的单向链表中的重复数字节点,并释放这些节点。其中head为单向链表头指针*/
{
NUMNODE *p1,*p2,*p3;
p1=p2=head;
    while(p1->next!=NULL)
    {
    p2=p1;
    p3=p1->next;
    if(strcmp(p1->num,p3->num)==0)
    {
    p1->next=p3->next;
            continue;
   }
    p1=p2->next;
    }
return head;
}
int main()
{
NUMNODE *h[10], *t[10];        // 存储分发集合的头指针数组和尾指针数组
NUMNODE *head;                  // 存储需要排序数据的单向链表头指针
char *string = "123,987,003,632,123";  // 需要排序的数据
int i;

head = createNumList(string);  /* 根据初始字符串,将排序数据存放在单向链表中,本例子形成的单向链表为 */
for (i=2;i>=0;i--)    // 从"个位关键字"开始,进行反复的分发和收集
{
distribute(head, h, t, i);  //将单向链表中的数字节点分发到集合中
/*for(int lop=0;lop<10;lop++)
{
printNumList(h[lop]);
}*/
head = collect(h, t); //将集合中的数字节点收集为一个新的单向链表
}
//printNumList(head);
head = delRepeatedNum(head);  //删除"升序排列"链表的重复数字节点 
printNumList(head);   //打印链表
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值