經常使用排序算法(九)——桶排序

         桶排序算是計數排序的一種改進和推廣,可是網上有許多資料把計數排序和桶排序混爲一談。其實桶排序要比計數排序複雜許多。 算法

         對桶排序的分析和解釋借鑑這位兄弟的文章(有改動):http://hxraid.iteye.com/blog/647759
       桶排序的基本思想: 數組

        假設有一組長度爲N的待排關鍵字序列K[1….n]。首先將這個序列劃分紅M個的子區間(桶) 。而後基於某種映射函數 ,將待排序列的關鍵字k映射到第i個桶中(即桶數組B的下標 i) ,那麼該關鍵字k就做爲B[i]中的元素(每一個桶B[i]都是一組大小爲N/M的序列)。接着對每一個桶B[i]中的全部元素進行比較排序(可使用快排)。而後依次枚舉輸出B[0]….B[M]中的所有內容便是一個有序序列。bindex=f(key) 其中,bindex 爲桶數組B的下標(即第bindex個桶), k爲待排序列的關鍵字。桶排序之因此可以高效,其關鍵在於這個映射函數,它必須作到:若是關鍵字k1 函數

         舉個栗子:
                   圖片描述性能

        假如待排序列K= {4九、 38 、 3五、 97 、 7六、 73 、 2七、 49 }。這些數據所有在1—100之間。所以咱們定製10個桶,而後肯定映射函數f(k)=k/10。則第一個關鍵字49將定位到第4個桶中(49/10=4)。依次將全部關鍵字所有堆入桶中,並在每一個非空的桶中進行快速排序後獲得如圖所示。只要順序輸出每一個B[i]中的數據就能夠獲得有序序列了。 code

        桶排序分析: orm

        桶排序利用函數的映射關係,減小了幾乎全部的比較工做。實際上,桶排序的f(k)值的計算,其做用就至關於快排中劃分,希爾排序中的子序列,歸併排序中的子問題,已經把大量數據分割成了基本有序的數據塊(桶)。而後只須要對桶中的少許數據作先進的比較排序便可。blog

         對N個關鍵字進行桶排序的時間複雜度分爲兩個部分:排序

           (1) 循環計算每一個關鍵字的桶映射函數,這個時間複雜度是O(N)。索引

           (2) 利用先進的比較排序算法對每一個桶內的全部數據進行排序,其時間複雜度爲 ∑ O(Ni*logNi) 。其中Ni 爲第i個桶的數據量。圖片

         很顯然,第(2)部分是桶排序性能好壞的決定因素。儘可能減小桶內數據的數量是提升效率的惟一辦法(由於基於比較排序的最好平均時間複雜度只能達到O(N*logN)了)。所以,咱們須要儘可能作到下面兩點:

           (1) 映射函數f(k)可以將N個數據平均的分配到M個桶中,這樣每一個桶就有[N/M]個數據量。

           (2) 儘可能的增大桶的數量。極限狀況下每一個桶只能獲得一個數據,這樣就徹底避開了桶內數據的「比較」排序操做。固然,作到這一點很不容易,數據量巨大的狀況下,f(k)函數會使得桶集合的數量巨大,空間浪費嚴重。這就是一個時間代價和空間代價的權衡問題了。

        對於N個待排數據,M個桶,平均每一個桶[N/M]個數據的桶排序平均時間複雜度爲:

        O(N)+O(M(N/M)log(N/M))=O(N+N(logN-logM))=O(N+NlogN-N*logM)

        當N=M時,即極限狀況下每一個桶只有一個數據時。桶排序的最好效率可以達到O(N)。

        總結: 桶排序的平均時間複雜度爲線性的O(N+C),其中C=N*(logN-logM)。若是相對於一樣的N,桶數量M越大,其效率越高,最好的時間複雜度達到O(N)。 固然桶排序的空間複雜度 爲O(N+M),若是輸入數據很是龐大,而桶的數量也很是多,則空間代價無疑是昂貴的。此外,桶排序是穩定的。

        實現代碼:

public class BucketSort { 
	public static void bucketSort(int[] arr) { 
		if(arr == null & arr.length == 0) 
			return ; 
		
		int bucketNums = 10; //這裏默認爲10,規定待排數[0,100) 
		List> buckets = new ArrayList>(); //桶的索引 
		
		for(int i=0; i) { 
			buckets.add(new LinkedList()); //用鏈表比較合適 
		} 
		
		//劃分桶 
		for(int i=0; i) { 
			buckets.get(f(arr[i])).add(arr[i]); 
		} 
		
		//對每一個桶進行排序 
		for(int i=0; i) { 
			if(!buckets.get(i).isEmpty()) { 
				Collections.sort(buckets.get(i)); //對每一個桶進行快排 
			} 
		} 
		
		//還原排好序的數組 
		int k = 0; 
		for(List bucket : buckets) { 
			for(int ele : bucket) { 
				arr[k++] = ele; 
			} 
		} 
	} 
	
	/** 
	* 映射函數 
	* @param x 
	* @return 
	*/ 
	public static int f(int x) { 
		return x / 10; 
	} 
}
相關文章
相關標籤/搜索