排序算法(七)非比較排序:計數排序、基數排序、桶排序

 

前面講的是比較排序算法,主要有冒泡排序選擇排序插入排序歸併排序堆排序快速排序等。html

非比較排序算法:計數排序基數排序桶排序。在必定條件下,它們的時間複雜度能夠達到O(n)。git

一,計數排序(Counting Sort)

(1)算法簡介

計數排序(Counting sort)是一種穩定的排序算法。計數排序使用一個額外的數組C,其中第i個元素是待排序數組A中值等於i的元素的個數。而後根據數組C來將A中的元素排到正確的位置。它只能對整數進行排序。算法

(2)算法描述和實現

  1. 獲得待排序數的範圍(在這裏增長了上界和下界);
  2. 統計數組中每一個值爲i的元素出現的次數,存入數組C的第i項;
  3. 對全部的計數累加(從C中的第一個元素開始,每一項和前一項相加),計算獲得每一個元素在排序後數組中的結束位置;
  4. 反向填充目標數組:將每一個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1

實現數組

 1 public static void countSort(int[] array, int downBound, int upperBound) {  2     int[] countArray = new int[upperBound - downBound + 1];  3     if (upperBound < downBound)  4         return;  5     for (int i = 0; i < array.length; i++) {  6         countArray[array[i] - downBound]++;  7  }  8     int posSum = 0;  9     for (int i = 0; i < upperBound - downBound + 1; i++) { 10         posSum += countArray[i]; 11         countArray[i] = posSum; 12  } 13     int[] result = new int[array.length]; 14     for (int i = array.length - 1; i >= 0; i--) { 15         result[countArray[array[i] - downBound] - 1] = array[i]; 16         countArray[array[i] - downBound]--; 17  } 18     for (int i = 0; i < array.length; i++) { 19         array[i] = result[i]; 20  } 21 }

(3)算法分析

當輸入的元素是n 個0到k之間的整數時,它的運行時間是 O(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序算法。因爲用來計數的數組C的長度取決於待排序數組中數據的範圍(等於待排序數組的最大值與最小值的差加上1),這使得計數排序對於數據範圍很大的數組,須要大量時間和內存(若是數據比較分散,則在countArray中實際上是有大量0的,佔用不少空間)。函數

最佳狀況:T(n) = O(n+k)
最差狀況:T(n) = O(n+k)
平均狀況:T(n) = O(n+k)spa

二,桶排序(Bucket Sort)

桶排序是計數排序的升級版。它利用了函數的映射關係,高效與否的關鍵就在於這個映射函數的肯定。.net

(1)算法簡介

桶排序 (Bucket sort)的工做的原理:假設輸入數據服從均勻分佈,將數據分到有限數量的桶裏,每一個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排。code

(2)算法描述和實現

  1. 設置一個定量的數組看成空桶;
  2. 遍歷輸入數據,而且把數據一個一個放到對應的桶裏去;
  3. 對每一個不是空的桶進行排序;
  4. 從不是空的桶裏把排好序的數據拼接起來。

實現htm

 1 public static void bucketSort(int[] arr){  2       
 3     int max = Integer.MIN_VALUE;  4     int min = Integer.MAX_VALUE;  5     for(int i = 0; i < arr.length; i++){  6         max = Math.max(max, arr[i]);  7         min = Math.min(min, arr[i]);  8  }  9       
10     //桶數 
11     int bucketNum = (max - min) / arr.length + 1; 12     ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); 13     for(int i = 0; i < bucketNum; i++){ 14         bucketArr.add(new ArrayList<Integer>()); 15  } 16       
17     //將每一個元素放入桶 
18     for(int i = 0; i < arr.length; i++){ 19         int num = (arr[i] - min) / (arr.length); 20  bucketArr.get(num).add(arr[i]); 21  } 22       
23     //對每一個桶進行排序 
24     for(int i = 0; i < bucketArr.size(); i++){ 25  Collections.sort(bucketArr.get(i)); 26  } 27

 下圖給出了對{ 29, 25, 3, 49, 9, 37, 21, 43 }進行桶排序的簡單演示過程blog

(3)算法分析

桶排序最好狀況下使用線性時間O(n),桶排序的時間複雜度,取決與對各個桶之間數據進行排序的時間複雜度,由於其它部分的時間複雜度都爲O(n)。很顯然,桶劃分的越小,各個桶之間的數據越少,排序所用的時間也會越少。但相應的空間消耗就會增大。

最佳狀況:T(n) = O(n+k)
最差狀況:T(n) = O(n+k)
平均狀況:T(n) = O(n2)

三,基數排序(Radix Sort)

(1)算法簡介

基數排序是按照低位先排序,而後收集(就是按低位排序);再按照高位排序,而後再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最後的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。基數排序基於分別排序,分別收集,因此是穩定的。

(2)算法描述和實現

  1. 取得數組中的最大數,並取得位數;
  2. arr爲原始數組,從最低位開始取每一個位組成radix數組;
  3. 對radix進行計數排序(利用計數排序適用於小範圍數的特色);
 1 public static void radixSort(int[] array, int maxDigit) {  2     int len = array.length;  3     int digitCount = 1;  4     int digitDev = 1;  5     int[] tmp = new int[len];  6     int[] count = new int[10];  7     while (digitCount <= maxDigit) {  8         Arrays.fill(count, 0);  9         Arrays.fill(count, 0); 10         for (int i = 0; i < len; i++) { 11             count[(array[i] / digitDev) % 10]++; 12  } 13         int sum = 0; 14         for (int i = 1; i < 10; i++) { 15             count[i] = count[i] + count[i - 1]; 16  } 17         for (int i = len - 1; i >= 0; i--) { 18             tmp[count[(array[i] / digitDev) % 10] - 1] = array[i]; 19             count[(array[i] / digitDev) % 10]--; 20  } 21         for (int i = 0; i < len; i++) { 22             array[i] = tmp[i]; 23  } 24         digitDev *= 10; 25         digitCount++; 26  } 27 }

下圖給出了對{ 329, 457, 657, 839, 436, 720, 355 }進行基數排序的簡單演示過程

(3)算法分析

最佳狀況:T(n) = O(n * k)
最差狀況:T(n) = O(n * k)
平均狀況:T(n) = O(n * k)

 

基數排序 vs 計數排序 vs 桶排序

這三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差別:

基數排序:根據鍵值的每位數字來分配桶
計數排序:每一個桶只存儲單一鍵值
桶排序:每一個桶存儲必定範圍的數值

參考:

http://www.cnblogs.com/eniac12/p/5332117.html

http://www.javashuo.com/article/p-gfdjryyu-gv.html

http://blog.csdn.net/wangqyoho/article/details/52584640

相關文章
相關標籤/搜索