前面講的是比較排序算法,主要有冒泡排序,選擇排序,插入排序,歸併排序,堆排序,快速排序等。html
非比較排序算法:計數排序,基數排序,桶排序。在必定條件下,它們的時間複雜度能夠達到O(n)。git
計數排序(Counting sort)是一種穩定的排序算法。計數排序使用一個額外的數組C,其中第i個元素是待排序數組A中值等於i的元素的個數。而後根據數組C來將A中的元素排到正確的位置。它只能對整數進行排序。算法
實現數組
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 }
當輸入的元素是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
桶排序是計數排序的升級版。它利用了函數的映射關係,高效與否的關鍵就在於這個映射函數的肯定。.net
桶排序 (Bucket sort)的工做的原理:假設輸入數據服從均勻分佈,將數據分到有限數量的桶裏,每一個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排。code
實現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
桶排序最好狀況下使用線性時間O(n),桶排序的時間複雜度,取決與對各個桶之間數據進行排序的時間複雜度,由於其它部分的時間複雜度都爲O(n)。很顯然,桶劃分的越小,各個桶之間的數據越少,排序所用的時間也會越少。但相應的空間消耗就會增大。
最佳狀況:T(n) = O(n+k)
最差狀況:T(n) = O(n+k)
平均狀況:T(n) = O(n2)
基數排序是按照低位先排序,而後收集(就是按低位排序);再按照高位排序,而後再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最後的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。基數排序基於分別排序,分別收集,因此是穩定的。
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 }進行基數排序的簡單演示過程
最佳狀況: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