描述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;
}