一、原理
桶排序的工作原理是把[0,1)区间划分为n个大小相同的子区间,这样的区间称为桶。然后将n个输入的数分发到各个桶中去。每个桶再个别的排序,然后按照次序把各个桶。
二、代码
方法一计数法:计数排序需要占用大量空间,它仅适用于数据比较集中的情况。
思想是:开辟和待排序数组最大值长度+1长度的计数数组,数组有多长,桶就有多少个,将相同的放到同一个桶里面。把 arr[i] 放到它输出数组中的位置上。
例如:a = {2, 8, 6,2}; 因为a[0] = 2,所以在buckets的第2个位置对应的零加一,即将buckets[2]的值+1。
public class BucketSort {
private int[] buckets; //桶用来记录元素值及次数
private int[] array;
public BucketSort(int range, int[] array){
this.buckets = new int[range];
this.array = array;
}
/**
* 排序
*/
public void sort(){
if(array != null && array.length > 1) {
for(int i = 0 ; i < array.length ; i++) {//把数组遍历一遍
buckets[array[i]]++;//将数组每个位置出现的数值的次数、位置记录下来,arr对应数字是多少就将它放到对应buckets的位置
}
}
}
/**
* 排序输出
*/
public void sortOut(){
for (int i = 0; i < buckets.length; i++){//buckets的每一项对应的就是数组中数值大小,所以i就是arr[x]
for(int j = 0 ; j < buckets[i] ; j++){//buckets[i]对应的就是出现相同的次数
System.out.print(i + " ");
}
}
}
public static void main(String[] args){
int[] array = {5, 7, 8, 10, 4, 8, 6, 4, 8, 0};
//取出最大值
int max = Integer.MIN_VALUE;
for(int i = 0; i < array.length; i++){
max = Math.max(max, array[i]);
}
//最坏情况下最小值是0 ,最大值是max。
//假如最大值是10,那么此时应该需要一个长度为max+1的bucket数组去装出现次数的值,才能保证不溢出。因为数组是从零开始的到10,一共11个数字。
int length = max + 1;
BucketSort bs = new BucketSort(length, array);
bs.sort();
bs.sortOut();//输出
}
}
分析:如果说最大值跟最小值相差不多,而且数值很大的情况下,只用max+1来作为数组长度是有些浪费和不严谨的。所以我们需要优化一下。优化后的是稳定的排序,相同的数值顺序不变。具体如下图所示:
优化后代码:
public class BucketSort3 {
public static int[] countSort(int[] array) {
//1.得到数列的最大值和最小值,并算出差值d
//取出最大值、最小值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int i = 0 ; i < array.length ; i++){//数值数组a
max = Math.max(max, array[i]);
min = Math.min(min, array[i]);
}
int d = max - min;
//2.创建统计数组并统计对应元素个数
int[] countArray = new int[d+1];//取最大,最小值算出差值因为数组是从零开始的,所以要装差值加一个数字
for(int i = 0 ; i < array.length ; i++) {
countArray[array[i] - min]++;
}
//3.统计数组做变形,后面的元素等于前面的元素之和
int sum = 0;
for(int i = 0; i < countArray.length ; i++) {//计数数组b
//90-90 = 0 -》0(sum = 0)+1(c[0]出现一次) = 1(sum),
// 1 + 0 (91并未出现 ——》0) c[1] = 1,
// 1 + 0(92并未出现 ——》0) c[2] = 1,
// 1 + 1(93出现 ——》1) = 2 c[3] = 2,
// 2 + 0(94未出现 ——》0) = 2 c[4] = 2,
// 2 + 0(95未出现 ——》0) = 2 c[5] = 2,
// 2 + 0(96未出现 ——》0) = 2 c[6] = 2,
// 2 + 0(97未出现 ——》0) = 2 c[7] = 2,
// 2 + 2(98出现 ——》2) = 4 c[8] = 4,
// 4 + 1(99出现 ——》1) = 2 c[9] = 5,
sum += countArray[i];
countArray[i] = sum;
}
//4.倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组
int[] sortedArray = new int[array.length];//排序数组c
for(int i = array.length - 1;i >= 0;i--) {
//每次循环计数会对应减一
//array[4] = 98 - 90 = 8 -》c[8] = 4 -> sortedArray[3] = 98
//array[3] = 99 - 90 = 9 -》c[9] = 5 -> sortedArray[4] = 99
//array[2] = 93 - 90 = 3 -》c[3] = 2 -> sortedArray[1] = 93
//array[1] = 98 - 90 = 8 -》c[8] = 3 -> sortedArray[2] = 98
//array[0] = 90 - 90 = 0 -》c[0] = 1 -> sortedArray[0] = 90
sortedArray[countArray[array[i] - min] - 1] = array[i];
countArray[array[i] - min]--;//并且把对应位置的计数减一因为已经排好一个了
}
//sortedArray[0] = 90 -> sortedArray[1] = 93 ->
// sortedArray[2] = 98 -> sortedArray[3] = 98 ->
// sortedArray[4] = 99
return sortedArray;
}
public static void main(String[] args) {
int[] array = new int[] {90,98,93,99,98};
int[] sortedArray = countSort(array);
System.out.println(Arrays.toString(sortedArray));
}
}
方法二 桶排序:可用于最大最小值相差较大的数据情况,要求数据的分布必须均匀,否则可能导致数据都集中到一个桶中。
public class BucketSort2 { public static void bucketSort(int[] arr){ int max = Integer.MIN_VALUE; int min = Integer.MAX_VALUE; for(int i = 0; i < arr.length; i++){ max = Math.max(max, arr[i]); min = Math.min(min, arr[i]); } //桶数 int bucketNum = (max - min) / arr.length + 1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); for(int i = 0; i < bucketNum; i++){ bucketArr.add(new ArrayList<>()); } //将每个元素放入桶 for(int i = 0; i < arr.length; i++){ int num = (arr[i] - min) / (arr.length); bucketArr.get(num).add(arr[i]); } //对每个桶进行排序 for(int i = 0; i < bucketArr.size(); i++){ Collections.sort(bucketArr.get(i)); } System.out.println(bucketArr.toString()); } public static void main(String[] args){ int[] array = {11, 6, 4, 1, 2, 3, 7, 9, 10}; bucketSort(array); } }
未完待续----------------