基數排序與計數排序

基數排序

【基本思想】

首先設立r個隊列,對列編號分別爲0~r-1,r爲待排序列中元素的基數(例如10進制數,則r=10),然後按照下面的規則對元素進行分配收集

1,先按最低有效位的值,把n個元素分配到上述的r個隊列中,然後從小到大將個隊列中的元素依次收集起來
2,再按次低有效位的值把剛收集起來的關鍵字分配到r個隊列中,重複收集工作
3,重複地進行上述分配和收集,直到最高有效位。(也就是說,如果位數爲d,則需要重複進行d次,d由所有元素中最長的一個元素的位數計量)

爲什麼這樣就可以完成排序呢?

以從小到大排序爲例

首先當按照最低有效位完成分配和收集後,此時得到的序列,是根據元素最低有效位的值從小到大排列的。

當按照次低有效位進行第二次分配和收集後,得到的序列,是先根據元素的次低有效位的值從小到大排列,然後再根據最低有效位的值從小到大排列。

以此類推,當按照最高有效位進行最後一次分配和收集後,得到的序列,是先根據元素的最高有效位的值從小到大排列,再根據次高有效位排列,。。。,再根據次低有效位,再根據最低有效位。自然就完成了每個元素的從小到大排列。

【空間複雜度】O(n+r)

【時間複雜度】

平均情況:O(d(n+r))

因爲每一趟分配的時間開銷是O(n),收集的開銷是O(r),因此執行d趟的時間開銷爲O(d(n+r)),通常d,r爲常數

最好情況:O(d(n+r))

最壞情況:O(d(n+r))

【穩定性】穩定

【優點】

穩定排序;時間複雜度可以突破基於關鍵字比較排序法的下界O(n)

【缺點】

需要額外的輔助空間

【算法實現】
  1. /** 
  2.  * 基數排序 
  3.  * @param arr 
  4.  */  
  5. public static void radixSort(int[] arr) {  
  6.     //獲得待排序列元素中的最大位數  
  7.     int maxBit = getMaxBit(arr);  
  8.     //對每一位分別進行分配和收集  
  9.     for (int bit = 1; bit <= maxBit; bit++) {  
  10.         //分配  
  11.         List<List<Integer>> buf = distribute(arr, bit);  
  12.         //收集  
  13.         collect(arr, buf);  
  14.     }  
  15. }  
  16. private static void collect(int[] arr, List<List<Integer>> buf) {  
  17.     int i = 0;  
  18.     //收集,依次將每個隊列中的元素讀出  
  19.     for (List<Integer> temp : buf) {  
  20.         for (int ele : temp) {  
  21.             arr[i++] = ele;  
  22.         }  
  23.     }  
  24. }  
  25. private static int getMaxBit(int[] arr) {  
  26.     int maxBit = 0;  
  27.     int len = 0;  
  28.     for (int ele : arr) {  
  29.         //利用字符串的length()方法獲得元素位數  
  30.         len = (ele + "").length();  
  31.         if (len > maxBit) {  
  32.             maxBit = len;  
  33.         }  
  34.     }  
  35.     return maxBit;  
  36. }  
  37. //分配  
  38. private static List<List<Integer>> distribute(int[] arr, int bit) {  
  39.     List<List<Integer>> buf = new ArrayList<>();  
  40.     //構建r個隊列  
  41.     for (int i = 0; i < 10; i++) {  
  42.         buf.add(new ArrayList<>());  
  43.     }  
  44.     for (int ele : arr) {  
  45.         int value = getValueByBit(ele, bit);  
  46.         //根據每個元素指定位數上的值,將元素存放到對應的隊列上  
  47.         buf.get(value).add(ele);  
  48.     }  
  49.     return buf;  
  50. }  
  51. //得到指定位數上的值  
  52. private static int getValueByBit(int ele, int bit) {  
  53.     //沒有該位,則返回0  
  54.     int value = 0;  
  55.     String temp = ele + "";  
  56.     if (temp.length() >= bit) {  
  57.         value = (int) (temp.charAt(temp.length() - bit) - '0');  
  58.     }  
  59.     return value;  
  60. }  

【本算法解讀】

算法針對的是十進制數,所以r=10

算法首先獲取到待排序列元素中的最大位數。然後按照基數排序的思想,對元素的每一位進行分配和收集。

分配distribute()方法:首先構建了r=10個隊列,對應編號即是0~9。根據給定的位數,得到待排序列中每個元素在該位上的值,若元素沒有該位,則返回0。根據每位上的值,將該元素放入相應的隊列。完成一次分配。

收集collect()方法:依次從r個隊列中讀出其中的元素值,並將其存入原始序列中,即完成了一次收集。

【舉個栗子】

對於待排序列413,10,8,28

首先獲取到待排序列的最大位數,即爲3。

然後根據每一位的值進行分配和收集,分配過程如下圖:


由於是10進制數,所以構造10個隊列,0~9。

首先從最低有效位開始,每個元素在該位上對應的值分別是3,0,8,8,根據該值,將對應元素放入對應的隊列。分配結束後,依次從每個隊列上讀取元素值存入原始序列。可以看到一次分配以後,元素已經按照最低有效位從小到大排列。繼續位數逐漸增大,直到最高有效位,重複上述操作即可完成排序。


計數排序

【基本思想】

計數排序是基於非比較排序,主要用於對於一定範圍內的整數進行排序。採用的是空間換時間的方法。

針對待排序列中的每一個元素x,得到序列中小於x的元素個數,有了這一信息可以直接把x放到最終的輸出序列的正確位置上。 計數排序之所以能做到線性時間複雜度是因爲使用了索引的思想。

計數排序對輸入的數據有附加的限制條件:
1、輸入的線性表的元素屬於有限偏序集S;
2、設輸入的線性表的長度爲n,|S|=k(表示集合S中元素的總數目爲k),則k=O(n)。
在這兩個條件下,計數排序的複雜性爲O(n)。
對於下面我即將給出的算法,它的限制條件是待排序列中的元素是有限個正整數(包括0,由於將元素值作爲數組下標),最大值用k=O(n)表示,元素個數用n表示。它利用元素的實際值來確定它們在輸出序列中的位置

【空間複雜度】O(n+k)

【時間複雜度】

平均情況:O(n+k)

最好情況:O(n+k)

最壞情況:O(n+k)

【穩定性】穩定

【優點】

穩定,在k值較小時突破了基於比較的排序算法下界

【缺點】

存在前提條件,需要大量額外空間,k值較大時效率很低

【算法實現】
  1. /** 
  2.  * 計數排序 
  3.  * @param arr 
  4.  */  
  5. public static void countSort(int[] arr) {  
  6.     int max = getMax(arr);  
  7.     int count[] = new int[max + 1];  //此時數組中元素值均被初始化爲0  
  8.     //在以元素值爲  
  9.     for (int ele : arr) {  
  10.         count[ele]++;  
  11.     }  
  12.     int k = 0;  
  13.     for (int i = 0; i <= max; i++) {  
  14.         for (int j = 0; j < count[i]; j++) {  
  15.             arr[k++] = i;  
  16.         }  
  17.     }  
  18. }  
  19.    //獲得待排元素中的最大值  
  20. private static int getMax(int[] arr) {  
  21.     int max = 0;  
  22.     for (int ele : arr) {  
  23.         if (ele > max) {  
  24.             max = ele;  
  25.         }  
  26.     }  
  27.     return max;  
  28. }  

【本算法解讀】

可以看到算法首先獲得待排序列元素中的最大值,然後構建最大值+1的長度的計數數組。遍歷待排序列的每個元素,並在以元素值爲下標的數組值中加1。然後遍歷計數數組,若對應下標的位置上值大於0(等於幾就表示有幾個元素),則表示存在有元素且其值爲下標的大小。將該元素添加到原始序列中。由於下標是從小到大的,所以對應得到的序列也是從小到大排列的。

【舉個栗子】

對於待排序列4,0,2,8,2

首先獲取最大值即爲0,然後構建8+1長度的計數數組。初始化時,計數數組中的值均爲0.如下圖所示:


將對應的元素值放在對應的下標裏,例如元素4,放在了下標爲4的位置裏,計數數組的值加1,表示其中有一個元素值爲下標的元素。待排序列中有兩個2,所以下標爲2的計數數組值是2,表示其中有兩個元素值爲下標的元素。

最後過遍歷計數數組,如,遍歷到0下標位置,數組值爲1,表示有一個元素,元素值是下標0,添加到原始序列中。遍歷到2下標位置,數組值爲2,表示有兩個元素,元素值均爲下標2,將兩個元素依次添加到原始序列中。以此類推,最終得到有序序列0,2,2,4,8。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章