基数排序

  基数排序(Radix Sorting),又称 桶排序(bucket sorting)是以前的各种排序方法彻底不一样的一中排序方法,在以前的排序方法中,主要是经过元素之间的比较和移动两种操做来实现排序的。基数排序不须要进行元素之间的比较,而是根据关键字的每一个位上的有效数字的值,借助“分配”和 “收集”两种操做来进行排序的一中内部排序方法。html

  在具体介绍基数排序算法前,首先先介绍两个两个关键词:单关键字多关键字。序列中任一记录的关键字均有 d 个份量 ki ki1  …… kid-1 构成,若 d 个份量中每一个份量都是一个独立的关键字,则文件是多关键字的(如扑克牌有两个关键字:点数和花色、汉子的笔画和拼音);不然是文件时单关键字的(如数值和字符串)。在多关键字的排序中,每一个关键字都能决定记录的大小,如扑克牌的花色黑桃比花色方片大;在花色相同的状况下,在比较点数的大小,如红桃 9 比红桃 8 大。 ki(0<= j < d) 是关键字中的其中一位(如字符串,十进制整数等)。多关键字中的每一个关键字的取值范围通常不一样,如扑克牌的花色取值只有 4 种,而点数则有 13 种。对于单关键字序排列能够利用多关键字排序的方法,只是单关键字的每位通常取值范围相同。git

  在介绍一下基数的概念。设单关键字的每一个份量的取值范围均为 C0 <= kj<= Crd-1 (0 <= j <=d),则每一个记录中份量的可能取值的个数 rd 称为基数。基数的选择和关键字的分解因关键字的类型而异:算法

  (1)若关键字是十进制整数,则按个、十等位进行分解,基数 rd = 10, C0 = 0,C9 = 9, d 为最长整数的位数;数据结构

  (2)若关键字是小写的由于字符串,则 rd = 26,C0 = 'a' ,C25 = 'z', d为字符串的最大长度。ide

  (3)在扑克牌花色和点数的排序中,花色的基数 rd1 = 4 ,而点数的 rd2 = 13, d 同时使用扑克牌的副数。spa

  

 

  基数排序时一种借助于过关键字排序的思想,将单关键字按基数分红“多关键字”进行排序的方法。好比字符串 "abcd"  "acsc"  "dwsc"  "rews" 就能够把每一个字符当作一个关键字,另外还有整数 425 、32一、23五、432也能够将每一个位上的数字做为一个关键字。指针

 

  通常状况下,假定有一包含 n 个对象的序列 { V0 , V1 , …… ,Vn-1  } ,且每一个对象 Vi   中包含 d 个关键字 ( ki1 ,ki2 , …… ,kid  ), 若是对于序列中任意两个对象 Vi  和 Vj    (0 <= i < j <= n - 1 ) 都知足: ( ki1 ,ki2 , …… ,kid  ) < ( kj1 ,kj2 , …… ,kjd  ) ,则称序列对关键字 ( k1 ,k2 , …… ,kd  )  有序。其中,k1  称为最高位关键字,k2  称为次高位关键字, kd  称为 最低位关键字。code

  基数排序方法有两种:最高位优先法(MSD:Most Significant Digit First)最低位优先法(LSD:Least Significant Digit First)。htm

  最高位优先法,简称MSD法:即先按 k1 排序分组,同一组中记录的关键字 k1 相等,在对各组按  k2 分红子组,以后对其余的关键字继续这样的排序分组,直到按最次位关键字  k 对各子组排序后。再将各组链接起来,获得一个有序序列。对象

  最低位优先法,简称LSD法: 即先从 kd 开始排序,在对  kd-1 进行排序,一次重复,直到对 k1 排序后便获得一个有序序列。  

 

 

  如今已下面的序列为例简述一下 MSD 方法和 LSD方法: ead, bed, dad, add, bee, abc, dbe, dae, cda, eba, ccd    共 n = 11 个字符串

    上面的每一个字符串每一个字符串包含 3 个字符,所以 d = 3, 这些字符的取值 为 { a, b, c, d, e}  共 5 种取值, rd = 5

    【注:这是针对这个例子而言 rd = 5,字符串的 rd 通常为 26 】

    MSD 方法的排序过程以下:   

      第一个字母排序: 将第一个字母相同的元素放在同一个队列,咱们就能够获得:

          第一个字母    元素

            a      add  abc

            b      bed  bee

            c      cda  ccd

            d      dad  dbe  dae

            e      ead  eda

      第二个字母排序:将上述队列中元素,第二个字母相同的元素放在同一个队列中,咱们能够获得

          第一个字母   第二个字母    元素

            a        b      abc

            a        d      add

            b        e      bed  bee

            c        c      ccd

            c        d      cda

            d        a      dad  dae

            d        b      dbe

            e        a      ead

            e        b      eba

      第三个字母排序:将上述队里中的元素,第三个字母相同的元素放在同一个队列中,咱们能够获得

          第一个字母   第二个字母    第三个字母   元素

            a        b        c     abc

            a        d        d     add

            b        e        d     bed

            b        e        e     bee

            c        c        d     ccd

            c        d        a     cda

            d        a        d     dad

            d        a        e     dae

            d        b        e     dbe

            e        a        d     ead

            e        b        a     eba

      因为每一个栏中只有一个元素,故从上到下链接起来就获得有序队列: abc, add, bed, bee, ccd, cda, dad, dae, dbe, ead, eba

 

    使用LSD排序过程以下:

    首先根据第三个字母排序,字母相同的放在一块儿,获得序列: cda, eba, abc, ead, bed, dad, add, ccd, bee, dbe, dae

    而后对获得的序列根据第二个字母排序获得:ead, dad, dae, eba, abc, dbe, ccd, cda, add, bed, bee

    最后获得的序列根据第一个字母排列获得:  abc, add, bed, bee, ccd, cda, dad, dae, dbe, ead, eba

 

   咱们能够看出使用LSD排序方法排序结果和 MSD排序方法是同样的,只是排序过程当中元素交换次序有些区别。MSD 是对在上一次分配好的队列(或初始序列)中对元素进行分配排序,每一次分配的元素愈来愈少,总数不变,可是分配次数增长;LSD是对上一次分配获得的序列,收集后再进行分配排序,每一次分配元素和次数均相同。 

 

 

  上面讲得是关于基数排序的基本思路和方法,下面咱们以另一个例子讲一下基数排序的实现过程。

   咱们以数值为例,先假定每一个数值 只有两位,所以数值包括 d = 2 个份量, 每一个数值的取值范围是 0 ~ 9 ,共 rd = 10 种,如数值:

      73, 22, 93, 43, 55, 14, 28, 65, 39,81

  首先根据个位数的数值,对每一个数值查询最末位的值,将它们分配到编号0 ~ 9 的队列中

  

    个位数     数值

     0      

     1      81 

     2      22      

     3      43 93 73

     4      14

     5      65 55

     6      

     7

     8      28

     9      39

   将上面的队里的数值从新串起来,若是是链式队列的话,这个过程将会很是简单,只要把指针链接起来就行了。咱们获得新的序列:

    81, 22, 43, 93, 73, 14, 65, 55, 28, 39

   接着对十位进行一次分配,咱们能够获得下面的结果:  

    十位数     数值

     0      

     1      14 

     2      22 28     

     3      39

     4      43

     5      55

     6      65

     7      73

     8      81

     9      93

  咱们将这些队列中的数值从新串起来,获得序列: 14, 22, 28, 39, 43, 55, 65, 73, 81, 93

  这个时候整个序列已经排序完毕,若是排序的对象有三位数以上,则继续进行以上的动做直至最高位为止。

  LSD的基数排序使用与位数小的数列,若是位数多的话,使用MSD的效率会比较好,MSD的方式刚好 与LSD相反,由最高位为基底进行分配其余的演算方式都相同。

  对于记录的长度不一样的序列,经过在记录前面加上相应数据类型最低位来进行处理,如数值类列前加0,字符串类型前加空字符。

          

  参考代码:(这是以LSD方式实现的)

 

 1 #include <stdio.h>
 2 
 3 #define MAX_NUM 80
 4 
 5 int main(int argc, char* argv[])
 6 {
 7     int data[MAX_NUM];   // 存储数据 
 8     int temp[10][MAX_NUM];  // 队列表 
 9     int order[10]={    0};   // 用于记录队列的信息 
10     
11     int n;    // 待排序个数 
12     int i,j,k;  // 用于排序 
13     int d;  // 用于表示待排序输的位数 
14     int lsd;  // 用于记录某位的数值 
15     
16     k = 0;
17     d = 1;
18     
19     printf("输入须要排序的个数(<=80),待排序数(<10000): ");
20     scanf("%d",&n);
21 
22     if( n > MAX_NUM ) n = MAX_NUM;
23     
24     for(i = 0; i < n;i++)
25     {
26         scanf("%d",&data[i]);
27         data[i] %= 10000;
28     }
29         
30     
31     while(d <= 10000)
32     {
33         for(i = 0; i < n; i++)
34         {
35             lsd = (data[i]/d)%10;
36             temp[lsd][order[lsd]] = data[i];
37             order[lsd]++;
38         }
39         
40         printf("\n从新排列:");
41         
42         for(i = 0; i < 10; i++ )
43         {
44             if(order[i]!=0)
45             {
46                 for(j = 0; j < order[i]; j++ )
47                 {
48                     data[k] = temp[i][j];
49                     printf("%d ",data[k]);
50                     k++;
51                 }
52             }
53             order[i] = 0;
54         }
55         
56         d *= 10;
57         k = 0;
58     }
59     
60     printf("\n 排序后:");
61     for(i = 0; i <n; i++)
62         printf("%d ",data[i]);
63     printf("\n");
64     
65 
66 
67     return 0;
68 }
View Code

  

  代码运行结果如下面的数值列为例:  125 11 22 34 15 44 76 66 100 8 14 20 2 5 1  共 15 个元素

  运行截图:

  

 

  基数排序算法的效率和稳定性

  对于 n 个记录,执行一次分配和收集的时间为O(n+r)。若是关键字有 d 位,若是关键字有 d 位,则要执行 d 遍。 因此总的运算时间为 O(d(n+r))。可见不一样的基数 r 所用时间是不一样的。当 r 或 d 较小时,这种算法较为节省时间。上面讨论的是顺序表的基数排序,这个算法一样也是用与链式的记录排序,只是要求额外附加一下队列的头、尾指针。因此附加的存储量为 2r 个存储单元。待排序的记录以链表形式存储的,相对于顺序表,只是额外增长了 n 个指针域的空间。

  基数排序在分配过程当中,对于相同关键字的记录而言保持原有顺序进行分配,故基数排序时稳定的排序方法。

  

  

 

  注:主要参考彭军、向毅主编的 《数据结构与算法》

相关文章
相关标签/搜索