一.概念引入
有作者把計數排序也稱為桶排序(各個桶中元素的排序采用計數排序),得到數組C后直接從前往后遍歷,輸出數組值次數組下標,為0就不輸出(或者存入原數組,不穩定),不過筆者認為這種說法不嚴謹(一個很明顯的問題是輸出會是雙重for循環,不過也有那個意思,叫鴿巢排序也未嘗不可),因為桶排序要求輸入數據在[0,1)范圍內(計數排序要求整數;實際上要么全是整數,要么小數,便於划分桶),先把區間[0,1)划分成n個相同大小的子區間,稱為桶,然后將n個輸入數分布到各個桶中去。因為輸入數均勻且獨立分布在[0,1)上,所以,一般不會有很多數落在一個桶中的情況。為了得到結果,先對各個桶中的數進行排序,然后按次序把各桶中的元素列出來。
附上鴿巢排序核心源代碼:
public void pigeonSort(int[] array, int max) { int[] c = new int[max];//max是array數組中的最大值 for(int i=0; i<array.length; i++) c[array[i]]++; //c數組只是統計元素出現次數 int k = 0; for(int i=0; i<max; i++) for(int j=1; j<=c[i]; j++) array[k++] = i; }二.算法描述
例如要對大小為[1..1000]范圍內的n個整數A[1..n]排序,可以把桶設為大小為10的范圍,具體而言,設集合B[1]存儲[1..10]的整數,集合B[2]存儲(10..20]的整數,……集合B[i]存儲((i-1)*10, i*10]的整數,i = 1,2,..100。總共有100個桶。然后對A[1..n]從頭到尾掃描一遍,把每個A[i]放入對應的桶B[j]中。 然后再對這100個桶中每個桶里的數字排序,這時可用冒泡,選擇,乃至快排,一般來說任何排序法都可以。最后依次輸出每個桶里面的數字,且每個桶中的數字從小到大輸出,這樣就得到所有數字排好序的一個序列了。
下圖表示出了桶排序作用於有10個數的輸入數組上的操作過程。
三.算法的Java實現
import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; public class BucketSort { public static void bucketSort(double array[]) { int length = array.length; ArrayList arrList[] = new ArrayList[length]; /* * 每個桶是一個list,存放落在此桶上的元素 * 上次的基數排序我采用的是計數排序實現的,其實也可以用下面的方法,有興趣的讀者不妨一試(我認為太復雜) * 不過效率估計不高(采用了動態數組) */ //划分桶並填元素 for (int i = 0; i < length; i++) { //0.7到0.79放在第8個桶里,編號7;第一個桶放0到0.09 int temp = (int) Math.floor(10 * array[i]); if (null == arrList[temp]) arrList[temp] = new ArrayList(); arrList[temp].add(array[i]); } // 對每個桶中的數進行插入排序 for (int i = 0; i < length; i++) { if (null != arrList[i]) { //此處排序方法不定,不過越快越好,除了三大線性排序外,都沒有Collections //和Arrays里的sort好,因為這是調優后的快拍 //Arrays里也有,在基數排序里用過copyOf和fill方法 Collections.sort(arrList[i]); } } //輸出類似鴿巢排序 int count = 0; for (int i = 0; i < length; i++) { if (null != arrList[i]) { Iterator iter = arrList[i].iterator(); while (iter.hasNext()) { Double d = (Double) iter.next(); array[count] = d; count++; } } } } /* * 每個元素滿足0<=array[i]<1,貌似還要長度相同, * 若是相同小數位(digit),則可以把小數搞為整數,最后再除以10^digit * 可以Random.nextInt(101)/100 */ public static void main(String[] args) { double array[] = { 0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68 }; bucketSort(array); for (int i = 0; i < array.length; i++) System.out.print(array[i] + " "); System.out.println(); } }四.算法應用
在面試的海量數據處理題目中,如對每天數以億計的數據進行排序,直接排序即使采用nlgn的算法,依然是一件很恐怖的事情,內存也無法容納如此多的數據,這時桶排序就可以有效地降低數據的數量級,再對降低了數量級的數據進行排序,可以得到比較良好的效果。另外也有說桶排序對元組排序,個人認為還是基數排序處理元組比較好,畢竟本身就是多關鍵字排序,只需要把比較單個數字(有興趣的參看我的這一篇《基於計數排序的基數排序》http://www.cnblogs.com/hxsyl/p/3210647.html)換成比較單個元組元素就好。