桶式排序不再是一种基于比较的排序方法,他是一种非常巧妙的排序方式,但这种排序方式需要满足下面两个要求:
1、待排序列的所有值处于一个可枚举的范围内。
2、待排序列所在的这个可枚举范围不应该太大,否则排序开销太大。
下面将介绍桶式排序的详细过程,以如下待排序列为例 。
5, 4, 2, 4, 1
这个待排序列处于0,1,2,3,4,5这个可枚举的范围内,而且枚举范围很小,正适合桶式排序。
具体步骤如下:
1、对这个可枚举范围构建一个 buckets 数组,用于记录“落入”每个桶中的元素个数,于是可以得到如下图所示的buckets数组:
buckets数组:
2、按如下公式对上图所示的buckets数组的元素进行重新计算。
buckets[i] = buckets[i] + buckets[i - 1] (其中 1 <= i <= buckets.length)
即可得到如下图所示的buckets数组:
原buckets数组:
新buckets数组:
桶式排序的巧妙之处就在于:重新计算后的buckets数组元素保存了“落入”当前桶和“落入”前面所有桶中元素的总数目,而且定义的桶本身就是从大到小且相差为 1 排列的。这样就可以得到一个结论:每个新的buckets数组元素的中它应该小于,等于“落入”当前桶中元素的个数。也就是说,“落入”当前桶中的元素在有序序列中应该排在新buckets数组元素值所确定的位置。
就那上面的新buckets来讲:
元素值为 0 时:此时桶内的元素个数为 0 ,也就是说“落入”当前桶的元素加上“落入”当前桶前面的桶的元素个数总数为 0 ,说明“落入”前面桶元素个数为 0,“落入”当前桶元素的个数也为 0 。为 0 说明什么,说明没有啊。也就是说元素0没有,数组中没有 0 这个枚举。
元素值为 1 时:此时桶内的元素个数为 1,也就是说此时只有它本身,不是吗?所有它排第。
元素值为 2 时:此时桶内的元素个数为 2,也就是说除了他本身,前面还有一个元素,那此时它排第2。
元素值为 3 时:此时桶内的元素个数为 2,也就是说除了他本身,前面还有一个元素,那此时它排第2。不对啊,前面不是元素值2排在第二位么?那它也排第二?
显然这里 3 > 2(元素值大于元素落入当前桶的个数跟落入当前桶前面桶的个数和); 不满足 每个新的buckets数组元素的中它应该小于,等于“落入”当前桶中元素的个数这句话。
……
下面程序实现了桶式排序:
public class BucketsSort {
/**
* 桶式排序
* @param data : 待排数组
* @param min :待排数组中最小值
* @param max :待排数组中最大值 +1
*
* @see data.length <= (max - min)
*/
public static void bucketSort(DataWrap[] data, int min, int max){
System.out.println( "-开始排序-");
int arrayLength = data.length;
DataWrap[] tempArr = new DataWrap[arrayLength];
//buckets数组相当于定义了max - min个桶
//buckets数组用于记录待排序元素的信息
//max 是序列数组中 (最大值+1)
int[] buckets = new int[max - min];
//计算序列数组中各个元素出现的次数
for(int i = 0; i < arrayLength; i ++){
buckets[ data[i].data - min] ++;
}
System.out.println( Arrays.toString(buckets));
//将buckets数组里面的每一项等于前面一项和当前项的和
//也是将data数组中每一数据项应该放的位置存入buckets中
//也就是说当下面的这个for循环执行完成后buckets数组中放的是索引值放入序列数组中的最终位置
for(int j = 1; j < max - min; j ++){
buckets[j] = buckets[j] + buckets[j - 1];
}
System.out.println( Arrays.toString(buckets));
System.arraycopy(data, 0, tempArr, 0, arrayLength);
//将data数组中的数组放在最终位置的临时数组中
for(int k = arrayLength - 1; k >= 0; k --){
data[ --buckets[ tempArr[k].data - min ] ] = tempArr[k];
}
}
public static void main(String[] args){
DataWrap[] data = {new DataWrap(9,""),
new DataWrap(5,"*"),
new DataWrap(-2,""),
new DataWrap(-2,"*"),
new DataWrap(-3,""),
new DataWrap(8,""),
new DataWrap(5,""),
new DataWrap(7,""),
new DataWrap(3,"*"),
new DataWrap(3,""),
new DataWrap(1,"")};
System.out.println( "-排序前-"+Arrays.toString(data));
bucketSort(data, -3, 9+1);
System.out.println( "-排序后-"+Arrays.toString(data));
}
}
桶式排序是一种非常优秀的排序算法,时间效率极高,它只需要进行两轮遍历。
桶式排序的空间开销比较大,从上面的实现 程序来看,它需要一个buckets数组来存放元素最终的存放位置和tempArray这样一个数组来临时存放待排序数组。
桶式排序从上面的输出结果来看是稳定的。