Skip to content

桶排序

桶排序算法回顾

题目

示例1

输入: nums = [4,0,1,2,0,5]
输出: [0,0,1,2,4,5]

解题思路

桶排序(Bucket Sort)的原理很简单,它是将数组分到有限数量的桶子里。是一种非比较的排序方法

在了解桶排序之前,先了解计数排序

其中计数排序思想如下:

假设待排序的数组a中共有N个整数,并且已知数组a中数据的范围[0, MAX)。在桶排序时,创建容量为MAX的桶数组r,并将桶数组元素都初始化为0;将容量为MAX的桶数组中的每一个单元都看作一个"桶"。 在排序时,逐个遍历数组a,将数组a的值,作为"桶数组r"的下标。当a中数据被读取时,就将桶的值加1。例如,读取到数组a[3]=5,则将r[5]的值+1。

Java代码1

java

public class BucketSort2 {
    public static void main(String[] args) {
        int[] arr = {4, 0, 1, 2, 0, 10};
        bucketSort2(arr);
        for (Integer i : arr) {
            System.out.print(i);
        }
    }

    public static void bucketSort2(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
        }
        int[] bucket = new int[max + 1];
        for (int i = 0; i < arr.length; i++) {
            bucket[arr[i]]++;
        }
        int index = 0;
        for (int j = 0; j < bucket.length; j++) {
            while (bucket[j]-- > 0) {
                arr[index++] = j;
            }
        }
    }
}

桶排序可以看做是计数排序的扩展,在计数排序中,每个桶只存储相同的元素

而桶排序中每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素存储到各个对应的桶中

之后对每个桶中的元素进行排序

最后将非空桶中的元素逐个放入原序列中

桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序就会失效

桶排序的稳定性取决于桶内部使用的排序算法

Java代码2

java
import java.util.ArrayList;
import java.util.Collections;

public class BucketSort {
    public static void main(String[] args) {
        int[] arr = {4, 0, 1, 2, 0, 5};
        bucketSort(arr);
        for (Integer i : arr) {
            System.out.print(i);
        }
    }

    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; //保证每个桶存的区间范围平均,+1代表余数补1个桶
        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); //确定arr[i]存储在哪个桶
            bucketArr.get(num).add(arr[i]);
        }

        // 对每个桶进行排序
        for (int i = 0; i < bucketArr.size(); i++) {
            Collections.sort(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);
            }
        }
    }
}

时间复杂度

对于待排序序列大小为N,共分为M个桶,主要步骤有:

  • N次循环,将每个元素装入对应的桶中
  • M次循环,对每个桶中的数据进行排序(平均每个桶有N/M个元素)

一般使用较为快速的排序算法,时间复杂度为O(nlogn),实际的桶排序过程是以链表形式插入的

整个桶排序的时间复杂度为:

O(N)+O(M*(N/M*log(N/M)))=O(N*(log(N/M)+1))

当N=M时,复杂度为O(N)

空间复杂度

O(N+M)