桶排序(計數排序)原理以及java實現

原來我所介紹的排序都是基於比較的排序,例如快排、堆、歸併等等,而桶排序不是基於比較的排序,而是一種基於的是數據狀況的排序,桶排序其實是已知要排序的數據所在的區間,把該區間均勻分成n個桶,這n個桶包含要排序的數的所有結果,我們把相應的結果放到相應的桶裏,然後再按次序把每個桶裏面的數據倒到一個數組裏,這個數據就有序了。很明顯,他的時間複雜度是O(n)和空間複雜度是O(n),因爲桶排序的桶(就是一種容器)可以使任意的數據結構來完成,所以其可以做呈穩定的比如把桶做成隊列這種結構,桶排序就是一種概念,實現這種感念的方式有兩種,一種叫做基數排序,一種叫做計數排序。

其實桶排序是一種思想,簡單桶排序也就是計數排序,下面來看桶排序的簡單實現:

public class BucketSort {
	public static void bucketSort(int[] a){
		//一樣是先判斷是否要進行排序
		if(a == null && a.length<2){
			return;
		}
		//最大值先初始化爲最小值
		int max = Integer.MIN_VALUE;
		//拿到這組數據的最大值
		for(int i = 0;i < a.length; i++){
			max = Math.max(max, a[i]);
		}
		//桶的數量是這組數據最大值+1爲得就是能裝下該範圍的所有數據
		int[] bucket = new int[max + 1];
		//要是數據在這個桶裏就把這個桶進行+1操作表示這個桶裏裝了多少相同的數據
		for(int i = 0; i < a.length; i++){
			bucket[a[i]]++;
		}
		//然後把每個桶裏的數據按桶從小到大倒出來就歐卡了
		int i = 0;
		for(int j = 0; j < bucket.length; j++){
			while(bucket[j]-- > 0){
				a[i++] = j;
			}
		}
	}
	public static void main(String[] args) {
		//初始化存儲數據
		int [] table = new int[10]; 
		Random random = new Random();
		//隨機生成100個100以內的整數,並存入數組
		for(int i=0;i<10;i++){
			table[i] = random.nextInt(100);
		}
		//遍歷初始化數組
		System.out.print("原數組序列是:");
		for(int j:table){
			System.out.print(j+" ");
		}
		//堆排序
		bucketSort(table);
		//輸出經過堆排序之後的數組
		System.out.println();
		System.out.print("經過桶排序的到的序列是:");
		for(int j:table){
			System.out.print(j+" ");
		}
	}
}

運行結果:

下面來看一種基於桶排序思想的一道題:

就是給定一個數組,這個數組的數據的每個數據都巨long(其實也就是不讓你使用排序算法去解決這道問題),讓你求排序之後相鄰的兩個數據的最大差值。

emmm,剛看到這道題,很難受,讓求排序之後的相鄰數據的最大差值,還不讓你排序,有點變態,但是因爲不讓排序,但給定了一組數據,其實也就是知道了這組數據所處的狀態,也就是最值,長度等等,根據一組數據所處狀態然後再去分析,我們考慮到了可以使用桶排序的思想(但不是桶排序),我們可以拿到這組數據的最大值和最小值,其實也就是拿到它的區間,也能拿到這組數據的個數(數組長度假設是n),所以我們根據桶排序的思想把這組數據分成n+1組,每組數據之間的差值其實是有限的,也就是這組數據的長度-1,但因爲我們有n+1個桶,所以我們的桶必然就會有一個是空桶,一定有不相鄰的兩個非空桶,他們兩組數據之間的差值一定大於桶內數據的差值,所以,這個方法其實只需要把每個非空桶的最大值和最小值求出來,所有非空桶相鄰後面的最小值減去前面的最大值記爲所求解之一,所求解的最大值即爲所求。下面來看代碼:

public class T1 {
	public static int maxGap(int[] a){
		if(a == null || a.length<2){
			return 0;
		}
		//求這組數據的狀態,也就是長度,最大最小值
		int len = a.length;
		int min = Integer.MAX_VALUE;
		int max = Integer.MIN_VALUE;
		for(int i = 0; i < len; i++){
			min = Math.min(min, a[i]);
			max = Math.max(max, a[i]);
		}
		//如果最大值等於最小值說明等於0
		if(min == max){
			return 0;
		}
		//用三個數組來表示每個桶的有效狀態,包括是否爲空桶、最大最小值
		boolean[] hasNum = new boolean[len + 1];
		int[] mins = new int[len + 1];
		int[] maxs = new int[len + 1];
		int bid = 0;
		for(int i = 0;i<len;i++){
			//根據我們傳進來的元素,長度,最小值,最大值來去判斷我這個元素該裝哪個桶裏去
			bid = bucket(a[i],len,min,max);
			//把每個桶的最大值和最小值記錄下來
			mins[bid] = hasNum[bid]?Math.min(mins[bid], a[i]):a[i];
			maxs[bid] = hasNum[bid]?Math.max(maxs[bid], a[i]):a[i];
			//不管這段代碼執行的結果怎樣,這個桶是一定進了數據了,因爲是把一個數據放在一個桶裏,這是前提
			hasNum[bid] = true;
		}
		//記錄可能答案
		int res = 0;
		//上一個非空桶的最大值,初始值是已知的,因爲第一個桶一定是非空桶
		int lastMax = maxs[0];
		//遍歷所有桶
		int i = 1;
		for(;i<=len;i++){
			//要是非空桶的話
			if(hasNum[i]){
				//可能結果就是這個桶的最小值減去上個非空桶的最大值再和原來記錄的可能結果進行比較
				//保留最大的數
				res = Math.max(res, mins[i] - lastMax);
				//這個桶的最大值變爲意義上的“上個通”的最大值
				lastMax = maxs[i];
			}
		}
		return res;
	}
	//這個方法就是用來判斷這個數據到底該放在哪個桶裏
	//其實就是這個數據在這個單位長度裏(len/max-min就是指的是單位長度,也就是現在的數表示的就是桶號了),
	//i-min就是在這個桶序號
	public static int bucket(long i, long len, long min, long max) {
		return (int )((i-min)*len/(max-min));
	}
	public static void main(String[] args) {
		//初始化存儲數據
		int [] table = new int[10]; 
		Random random = new Random();
		//隨機生成10個100以內的整數,並存入數組
		for(int i=0;i<10;i++){
			table[i] = random.nextInt(100);
		}
		//遍歷初始化數組
		System.out.print("原數組序列是:");
		for(int j:table){
			System.out.print(j+" ");
		}
		System.out.print("最大差值是是:"+maxGap(table));
	}
}

運行結果:

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