桶数组排序,是一种空间换时间的算法。
简单的桶数组排序,要新建一个数组,其大小是待排序数字的最大值+1;
复杂的桶数组排序,是一个集合中放好多桶数组,计算桶的数组的数量,分别将一定范围的值放到桶数组中。
简单的桶数组排序
这种比较好理解,思路是《啊哈算法》算法书中的,作者也说真正的桶排序算法比这个难的多,但这种简单的更适合入门。
思路及步骤结合下图说明:
对于放着 5, 3,5,2,8 数组arr进行排序:
第一步:遍历数组arr,找到待排序数组中的最大值max;
第二步:新建一个数组,空间比最大值max大一些;
第三步:遍历待排序的数组arr,将数值做为新数组arr2的下标,进行自加;
第四步:遍历新数组arr2,哪个大于1就输出该处下标即可。
解释:第三步走完,如下图,arr2中,arr2[2],ar2r[3],arr2[8]处的值分别是1,arr[5]处的值是2,
然后进行第四步,遍历数组arr2,遇到不为0的就打印下标,就可以完成排序。
图来自《啊哈算法》
代码实现:
/**
* @Description: 桶数组排序,简单的
*/
public class BucketSort {
public static void main(String[] args) {
//调用方法测试
int arr[] = {5, 3, 5, 2, 8};
bucketSort(arr);
}
//桶数组排序
public static void bucketSort(int arr[]){
int len = arr.length;
//找到数组中最大值
int max = Integer.MIN_VALUE; //-2147483648
for (int i = 0; i < len; i++){
max = Math.max(arr[i], max);
}
//新建一个数组,空间为原数据最大值+1(这也是桶数组的缺点)
int arr2[] = new int[max+1];
//遍历待排序的数组,将数值做为新数组的下标,进行自加
for (int i = 0; i < len; i++){
arr2[arr[i]]++;
}
//遍历输出
for (int i = 0; i < max+1; i++){ //依次遍历数组arr2,
for (int j = 1; j <= arr2[i]; j++){ //出现几次就将桶的编号输出几次
System.out.println(i);
}
}
}
}
时间复杂度:
时间复杂度(平均): O(n)
时间复杂度(最坏): O(nlog2n)
空间复杂度 : O(m)
稳定性:稳定
优化: 在我跟同学说完这个简单的桶排序后,他说了一种优化一些的方法,很厉害,
意思是: 将待排序的数组,都减去最小值,这样就能减少桶数量,遍历输出时,再加上最小值就可以了。
这种比较适合 各个相差比较小 的数据,这样能极大地减少桶数量。
下边复杂的桶数组排序,比较适合散列大一点的,能够很好地减少空间。
复杂的桶数组排序
这种是一个集合中,放入好多桶,将一定范围的待排序放入桶中,然后对桶中数据进行排序,最后依次输出桶中数据即可。
思路及步骤结合下图说明:
第一步:找出待排序数组中的最大值max和最小值min;
第二步:使用ArrayList做为桶,然后里面还放ArrayList桶,大小为:(max-min)/arr.length+1;
第三步:遍历待排序的数组,将数据放到桶中;
第四步:将各个桶中数据进行排序;
第五步:遍历桶数组,输出。
(图来自网络,图中的桶数和代码计算不太一样)
代码实现
public class BucketSort2 {
public static void main(String[] args) {
int arr2[] = {18, 11, 28, 45, 23, 50};
bucketSort(arr2);
System.out.println(Arrays.toString(arr2));
}
//桶数组排序
public static void bucketSort(int arr[]) {
if (arr == null || arr.length == 0) {
return;
}
//计算最大值与最小值:将数组的值与最大、最小比较获得
int max = Integer.MIN_VALUE; //-2147483648
int min = Integer.MAX_VALUE; // 2147483647
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;
System.out.println("bucketNum:" + bucketNum);
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
bucketArr.add(new ArrayList<Integer>());
}
//将每个元素放入桶中
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.get(i));
}
//将桶中元素赋值到新序列
int index = 0;
for (int i = 0; i < bucketArr.size(); i++) {
for (int j = 0; j < bucketArr.get(i).size(); j++) {
arr[index++] = bucketArr.get(i).get(j);
}
}
}
}
时间复杂度
当输入的元素是0到k之间的整数时
平均时间复杂度:O(n + k)
最佳时间复杂度:O(n + k)
最差时间复杂度:O(n ^ 2)
空间复杂度:O(n * k)
稳定性:稳定
桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。
常用的方法
length —— 数组的属性;
length() —— String的方法;
size() —— 集合的方法;