23、内部排序之基数排序

一、基本思想

1、基数排序(Radix Sort)是一种借助于多关键字排序的思想对单逻辑关键字进行排序的方法,即先将关键字分解成若干部分,然后通过对各部分关键字的分别排序,最终完成对全部记录的排序。对记录关键字进行定义时,总是要规定其长度和组成的字符。这样,可以把d位关键字看作d个关键字组成的一个多关键字。

        基数排序首先把每个关键字看作为一个d元组:

            Ki=( Ki0, Ki1,…,Kid-1) 

设有n个记录的序列  {R1,R2,...,Rn},且每个记录Ri含有d个关键字(Ki1,Ki2,...,Kid),则称序列{R1,R2,…,Rn}对关键字(K1,K2,…,Kd)有序指:对任意两个记录Ri和Rj(1<=i<j<=n)都满足下列有序关系:

(Ki1,Ki2,...,Kid)<(Kj1,Kj2,...,Kjd)

其中K1为最高位关键字,Kd为最低位关键字。

例如,若关键字是3位数字组成,可以看作(k1,k2,k3)三个关键字组成。有5个字母组成的关键字可看成(k1,k2,k3,k4,k5)五个关键字组成。由数字组成的关键字有10个不同的取值,其“基”为10;字母组成的关键字有26个不同取值,其“基”为26。“基”指关键字取值的范围。

2、基数排序,一般有两种方法。一种是先对主关键字K0进行排序,将序列分成若干个子序列,每个子序列都有相同的K0值,然后分别就每个子序列对关键字K1进行排序,按K1值不同再把其分成若干个更小的子序列,依次重复,直到对Kd-2进行排序后,最后所有子序列依次联接在一起成为一个有序序列,我们称之为最高位优先法(MSD,Most Significiant Digit First);另一种是从最次位Kd-1优先排序,然后对高一位的Kd-2进行排序,依次重复,直至对K0进行排序后便成为一个有序序列,我们称之为最低位优先法LSD(Least Significant Digit First)。我们这里介绍的基数排序方法就是借助“分配”和“收集”两种操作对单逻辑关键字进行排序的一种方法。设关键字的基为rd,从关键字低位起,按关键字的不同值将序列中记录“分配”到rd个队列中,再从小到大顺序“收集”起来,如此重复d次。

3、我们用静态链表存储n个待排记录,令表头记录指向第一个记录。用链表表示队列。队列头、尾指针放入数组中。第一趟分配对最低数位关键字进行,改变记录的指针值将链表中的记录分配到链队列中去。第一趟收集是改变所有非空队列的队尾记录的指针域,令其指向下一个非空队列的头指针,重新将这几个队列中的记录链接成一个链表。接着进行第二趟分配和收集,依次,至对每个关键字都进行了分配的收集,完毕。

如图中所示:

4、复杂度

空间分析:空间复杂度为O(n+2rd)(n个指针域,2rd个队列指针)

时间分析:共进行d遍,每趟要分配n个记录,收集时调整rd个尾指针。因此,算法时间复杂度为O(d(n+rd)),当n比rd大得多时,O(d*n),若d为常数,则为O(n).

基数排序是稳定的。

5、静态链表

二、算法的C语言描述

1、存储结构

2、算法描述

三、算法的C语言实现

#include"stdio.h"

#include"stdlib.h"

#include"math.h"

#include"string.h"

#define OK 1

#defineMAX_NUM_OF_KEY 8

#define RADIX 10

#define MAX_SPACE10000

#define N 10 

typedef intKeyType;

typedef intInfoType;

typedef structRedType

{

KeyType key;

InfoTypeotherinfo;

}RedType;

typedef charKeysType; //定义关键字类型为字符型

typedef struct

{

KeyTypekeys[MAX_NUM_OF_KEY];

InfoTypeotheritems;

int next;

}SLCell;

typedef struct

{

SLCellr[MAX_SPACE]; //r[0]为头结点

int keynum;

int recnum;

}SLList;

typedef intArrType[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];

          printf("%c ",L.r[i].keys[j]);

         }

       printf("\n");

   }

 }

 int ord(char c)

 { // 返回k的映射(个位整数)

   return c-'0';

 }

 void Distribute(SLCell r[],int i,ArrTypef,ArrType e) // 算法10.15

 { // 静态键表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 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 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 print(SLList L)

 { // 按数组序号输出静态链表

   int i,j;

   printf("keynum=%drecnum=%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=%dnext=%d\n",L.r[i].otheritems,L.r[i].next);

   }

 }

 #define N 10

int main()

 {

   RedTyped[N]={{278,1},{109,2},{63,3},{930,4},{589,5},{184,6},{505,7},{269,8},{8,9},{83,10}};

   SLList l;

   InitList(l,d,N);

   printf("排序前(next域还没赋值):\n");

   print(l);

   RadixSort(l);

   printf("排序后(静态链表):\n");

   print(l);

return OK;

 }

发布了278 篇原创文章 · 获赞 31 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/hopegrace/article/details/104510421
今日推荐