此博客用于个人学习,来源于算法的书籍和网上的资料,对知识点进行一个整理。
1. 概述:
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
2. 算法:
- 创建这些桶,确定每一个桶的区间范围:区间跨度(大小) = (最大值-最小值)/ (桶的数量 - 1)
- 遍历原始数列,把元素对号入座放入各个桶中
- 每个桶内部的元素分别排序(显然,只有第一个桶需要排序)
- 遍历所有的桶,输出所有元素
3. 代码实现:
/**
* 桶排序
*/
public class BucketSort {
public static double[] bucketSort(double[] array){
//1.得到数列的最大值和最小值,并算出差值d
double max = array[0];
double min = array[0];
for (int i= 1; i<array.length; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
double d = max - min;
//2.初始化桶
int bucketNum = array.length;
ArrayList<LinkedList<Double>> bucketList = new ArrayList<LinkedList<Double>>(bucketNum);
for (int i = 0; i < bucketNum; i++){
bucketList.add(new LinkedList<Double>());
}
//3.遍历原始数组,将每个元素放入桶中
for (int i = 0; i < array.length; i++){
int num = (int)((array[i] - min) * (bucketNum- 1) / d);
bucketList.get(num).add(array[i]);
}
//4.对每个通内部进行排序
for (int i = 0; i < bucketList.size(); i++){
//JDK底层采用了归并排序或归并的优化版本
Collections.sort(bucketList.get(i));
}
//5.输出全部元素
double[] sortedArray = new double[array.length];
int index = 0;
for (LinkedList<Double> list : bucketList){
for (double element : list){
sortedArray[index] = element;
index++;
}
}
return sortedArray;
}
}
4. 特点:
-
优点:在取值范围不是很大的情况下,它的性能甚至快于 O(nlgn) 的排序算法,并且元素并不要求都为整型。
-
缺点:桶排序对数据的条件有特殊要求,适合元素值集合并不大的情况。
5. 算法分析:
-
时间复杂度:
第一步求数列最大最小值,运算量为 n。
第二步创建空桶,运算量为 m。
第三步遍历原始数列,运算量为 n。
第四步:一共有 m 个桶,每个桶内部使用了 O(nlogn)的排序算法做排序,每个桶的元素平均有 n/m 个(即:数据规模为 n/m),所以运算量为 m * (n/m) * log(n/m ) 。
第五步输出排序数列,运算量为 n。
加起来,总的运算量为 3n+m+ n/m * log(n/m ) * m = 3n+m+n(logn-logm) 。去掉系数,时间复杂度为:O(n+m+n(logn-logm))
-
空间复杂度:空桶占用的空间 + 数列在桶中占用的空间 = O(m+n)。
-
稳定性:由于其中使用了 list 的数据结构,使得大小相等的两个元素相对位置不变,是稳定的。