基數排序

  基數排序(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 個指針域的空間。

  基數排序在分配過程當中,對於相同關鍵字的記錄而言保持原有順序進行分配,故基數排序時穩定的排序方法。

  

  

 

  注:主要參考彭軍、向毅主編的 《數據結構與算法》

相關文章
相關標籤/搜索