[034]八大排序算法詳解——基數排序

基本思想

基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較。由於整數也可以表達字符串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是隻能使用於整數。
基數排序可以採用兩種方式:

  • LSD(Least Significant Digital):從待排序元素的最右邊開始計算(如果是數字類型,即從最低位個位開始)。
  • MSD(Most Significant Digital):從待排序元素的最左邊開始計算(如果是數字類型,即從最高位開始)。

我們以LSD方式爲例,從數組R[1..n]中每個元素的最低位開始處理,假設基數爲radix,如果是十進制,則radix=10。基本過程如下所示:

  1. 計算R中最大的元素,求得位數最大的元素,最大位數記爲distance;
  2. 對每一位round<=distance,計算R[i] % radix即可得到;
  3. 將上面計算得到的餘數作爲bucket編號,每個bucket中可能存放多個數組R的元素;
  4. 按照bucket編號的順序,收集bucket中元素,就地替換數組R中元素;
  5. 重複2~4,最終數組R中的元素爲有序。

算法實現

基數排序算法,Java實現,代碼如下所示:

01 public abstract class Sorter {
02      public abstract void sort(int[] array);
03 }
04  
05 public class RadixSorter extends Sorter {
06      
07      private int radix;
08      
09      public RadixSorter() {
10           radix = 10;
11      }
12      
13      @Override
14      public void sort(int[] array) {
15           // 數組的第一維表示可能的餘數0-radix,第二維表示array中的等於該餘數的元素
16           // 如:十進制123的個位爲3,則bucket[3][] = {123}
17           int[][] bucket = new int[radix][array.length];
18           int distance = getDistance(array); // 表示最大的數有多少位
19           int temp = 1;
20           int round = 1// 控制鍵值排序依據在哪一位
21           while (round <= distance) {
22                // 用來計數:數組counter[i]用來表示該位是i的數的個數
23                int[] counter = new int[radix];
24                // 將array中元素分佈填充到bucket中,並進行計數
25                for (int i = 0; i < array.length; i++) {
26                     int which = (array[i] / temp) % radix;
27                     bucket[which][counter[which]] = array[i];
28                     counter[which]++;
29                }
30                int index = 0;
31                // 根據bucket中收集到的array中的元素,根據統計計數,在array中重新排列
32                for (int i = 0; i < radix; i++) {
33                     if (counter[i] != 0)
34                          for (int j = 0; j < counter[i]; j++) {
35                               array[index] = bucket[i][j];
36                               index++;
37                          }
38                     counter[i] = 0;
39                }
40                temp *= radix;
41                round++;
42           }
43      }
44      
45      private int getDistance(int[] array) {
46           int max = computeMax(array);
47           int digits = 0;
48           int temp = max / radix;
49           while(temp != 0) {
50                digits++;
51                temp = temp / radix;
52           }
53           return digits + 1;
54      }
55      
56      private int computeMax(int[] array) {
57           int max = array[0];
58           for(int i=1; i<array.length; i++) {
59                if(array[i]>max) {
60                     max = array[i];
61                }
62           }
63           return max;
64      }
65 }

基數排序算法,Python實現,代碼如下所示:

01 class Sorter:
02     '''
03     Abstract sorter class, which provides shared methods being used by
04     subclasses.
05     '''
06     __metaclass__ = ABCMeta
07     
08     @abstractmethod  
09     def sort(self, array):
10         pass
11  
12 class RadixSorter(Sorter):
13     '''
14     Radix sorter
15     '''
16     def __init__(self):
17         self.radix = 10
18         
19     def sort(self, array):
20         length = len(array)
21         which_round = 1
22         bucket = [[0 for col in range(length)] for row in range(self.radix)]
23         distance = self.__get_distance(array)
24         temp = 1
25         while which_round<=distance:
26             counter = [0 for in range(self.radix)]
27             for in range(length):
28                 which = (array[i] // temp) % self.radix
29                 bucket[which][counter[which]] = array[i]
30                 counter[which] += 1
31             index = 0
32             for in range(self.radix):
33                 if counter[i]!=0:
34                     for in range(counter[i]):
35                         array[index] = bucket[i][j]
36                         index += 1
37             temp *= self.radix
38             which_round += 1
39             
40  
41     def __get_distance(self, array):
42         max_elem = self.__get_max(array)
43         digits = 0
44         temp = max_elem // self.radix
45         while temp != 0:
46             digits += 1
47             temp //= self.radix
48         return digits + 1
49     
50     def __get_max(self, array):
51         max_elem = array[0]
52         for in range(1len(array)):
53             if array[x]>max_elem:
54                 max_elem = array[x]
55         return max_elem

排序過程

假設待排序數組爲array = {94,12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49},數組大小爲20,我們以該數組爲例,
最大的數組元素的位數爲2,所以需要進行2輪映射(映射到對應的桶中),執行基數排序的具體過程,如下所示:

  • 數組原始順序

數組的原始順序,如下圖所示:
radixsorter-array-original
數組中存在的相同的元素(同一個待排序的數字出現大於1次),我們使用不同的背景顏色來區分(紅色背景表示第二次出現,靛青色表示第三次出現),如果一個元素只出現過一次,則我們就使用一種固定的顏色(淺綠色)表示。

    根據數組元素個位數字將數組中元素映射到對應的桶中(bucket)

我們使用的是十進制,基數(Radix)自然是10,根據數組元素個位數的,應該映射到10個桶中,映射後的結果,如圖所示:
radixsorter-bucket-1
在映射到桶的過程中,從左到右掃描原始數組。因爲映射到同一個桶中的元素可能存在多個,最多爲整個數組的長度,所以在同一個桶中,要保持進入桶中的元素的先後順序(先進的排在左側,後進的排在右側)。

  • 收集桶中元素,並在原始數組中原地替換,使數組中元素順序重新分佈

掃面前面已經映射到各個桶中的元素,滿足這樣的順序:先掃描編號最小的桶,桶中如果存在多個元素,必須按照從左到右的順序。這樣,將得到的數組元素重新分佈,得到一個元素位置重新分佈的數組,如圖所示:
radixsorter-array-after-collecting-1
這時,可以看到元素實際上是按照個位的數字進行了排序,但是基於整個元素來說並不是有序的。

  • 根據數組元素十位數字將數組中元素映射到對應的桶中(bucket)

這次映射的原則和過程,與前面類似,不同的是,這次掃描的數組是經過個位數字處理重新分佈後的新數組,映射後桶內的狀態,如圖所示:
radixsorter-bucket-2

  • 收集桶中元素,並在原始數組中原地替換,使數組中元素順序重新分佈

和前面收集方法類似,得到的數組及其順序,如圖所示:
radixsorter-array-after-collecting-2
我們可以看到,經過兩輪映射和收集過程,數組已經變成有序了,排序結束。

算法分析

  • 時間複雜度

設待排序的數組R[1..n],數組中最大的數是d位數,基數爲r(如基數爲10,即10進制,最大有10種可能,即最多需要10個桶來映射數組元素)。處理一位數,需要將數組元素映射到r個桶中,映射完成後還需要收集,相當於遍歷數組一遍,最多元素書爲n,則時間複雜度爲O(n+r)。所以,總的時間複雜度爲O(d*(n+r))。

  • 空間複雜度

設待排序的數組R[1..n],數組中最大的數是d位數,基數爲r。基數排序過程中,用到一個計數器數組,長度爲r,還用到一個r*n的二位數組來做爲桶,所以空間複雜度爲O(r*n)。

  • 排序穩定性

通過上面的排序過程,我們可以看到,每一輪映射和收集操作,都保持從左到右的順序進行,如果出現相同的元素,則保持他們在原始數組中的順序。

可見,基數排序是一種穩定的排序。

轉載鏈接:http://shiyanjun.cn/archives/823.html

發佈了33 篇原創文章 · 獲贊 14 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章