24、桶排序

内容来自刘宇波老师算法与数据结构体系课

1、MSD 与 Bucket

image
image

2、桶排序原理

image
image

3、桶排序实现一

/**
 * 桶排序
 */
public class BucketSort {

    private BucketSort() {
    }

    public static void sort(Integer[] arr, int B) {
        if (B <= 1) throw new IllegalArgumentException("B must be > 1");
        sort(arr, 0, arr.length - 1, B, new Integer[arr.length]);
    }

    /**
     * 基于 MSD 思路
     */
    private static void sort(Integer[] arr, int left, int right, int B, Integer[] temp) {
        if (left >= right) return;

        int minV = Integer.MAX_VALUE;
        int maxV = Integer.MIN_VALUE;
        for (int i = left; i <= right; i++) {
            minV = Math.min(minV, arr[i]);
            maxV = Math.max(maxV, arr[i]);
        }
        if (minV == maxV) return;
        // d 代表每个桶中的元素可能数
        int d = (maxV - minV + 1) / B + ((maxV - minV + 1) % B != 0 ? 1 : 0);

        int[] cnt = new int[B];
        int[] index = new int[B + 1];

        for (int i = left; i <= right; i++) cnt[(arr[i] - minV) / d]++;
        for (int i = 0; i < B; i++) index[i + 1] = index[i] + cnt[i];
        for (int i = left; i <= right; i++) {
            int p = (arr[i] - minV) / d;
            temp[left + index[p]] = arr[i];
            index[p] += 1;
        }
        System.arraycopy(temp, left, arr, left, right - left + 1);

        // index[0, B] 的最后一个区间是无效的
        // 需要遍历 index[0, B - 1] 中所有的区间
        sort(arr, left, left + index[0] - 1, B, temp);
        for (int i = 0; i + 1 <= B - 1; i++) {
            sort(arr, left + index[i], left + index[i + 1] - 1, B, temp);
        }
    }
}

4、更简单的桶排序

image
image

5、桶排序实现二

/**
 * 桶排序
 */
public class BucketSort {

    private BucketSort() {
    }

    public static void sort(Integer[] arr, int c) {
        // c 代表每个桶中的元素可能数
        if (c <= 0) throw new IllegalArgumentException("c must be > 0");

        int minV = Integer.MAX_VALUE;
        int maxV = Integer.MIN_VALUE;
        for (int num : arr) {
            minV = Math.min(minV, num);
            maxV = Math.max(maxV, num);
        }
        if (minV == maxV) return;

        int range = maxV - minV + 1;
        int B = range / c + (range % c != 0 ? 1 : 0);
        LinkedList<Integer>[] buckets = new LinkedList[B];
        for (int i = 0; i < B; i++) buckets[i] = new LinkedList<>();

        for (Integer num : arr) buckets[(num - minV) / c].add(num);
        for (int i = 0; i < B; i++) Collections.sort(buckets[i]);
        int index = 0;
        for (int i = 0; i < B; i++) {
            for (int num : buckets[i]) arr[index++] = num;
        }
    }
}

6、桶排序实现三

/**
 * 桶排序
 */
public class BucketSort {

    private BucketSort() {
    }

    public static void sort(Integer[] arr, int c) {
        // c 代表每个桶中的元素可能数
        if (c <= 0) throw new IllegalArgumentException("c must be > 0");

        int minV = Integer.MAX_VALUE;
        int maxV = Integer.MIN_VALUE;
        for (Integer num : arr) {
            minV = Math.min(minV, num);
            maxV = Math.max(maxV, num);
        }
        if (minV == maxV) return;

        int range = maxV - minV + 1;                  // arr 中的元素可能数
        int B = range / c + (range % c != 0 ? 1 : 0); // 根据元素可能数决定桶的个数
        Integer[][] buckets = new Integer[B][c];      // B 个桶, 每个桶的大小姑且为 c
        int[] size = new int[B];                      // size[i] 代表 buckets[i] 中的元素数量

        for (Integer num : arr) {
            int p = (num - minV) / c;
            Integer[] bucket = buckets[p];
            if (size[p] == bucket.length) {
                resize(buckets, p);
                bucket = buckets[p];
            }
            bucket[size[p]++] = num;
        }
        for (int i = 0; i < B; i++) InsertionSort.sort(buckets[i], 0, size[i] - 1);

        int index = 0;
        for (int i = 0; i < B; i++) {
            Integer[] bucket = buckets[i];
            for (int j = 0; j < size[i]; j++) arr[index++] = bucket[j];
        }
    }

    private static void resize(Integer[][] buckets, int p) {
        Integer[] bucket = buckets[p];
        Integer[] newBucket = new Integer[(int) (bucket.length * 1.5)];
        System.arraycopy(bucket, 0, newBucket, 0, bucket.length);
        buckets[p] = newBucket;
    }
}

7、复杂度分析

在第三版桶排序中

数据量是 N,数据范围是 range
每个桶有 c 范围个元素,共 range / c 个桶
假设数量比较平均的落到桶中,每个桶的元素个数大约是 Nc / range
每个桶采用插入排序:(Nc / range)^2

整体:(range / c) * (Nc / range)^2 = (N^2 * c / range)
在我的测试用例中,N 约等于 range,最终的复杂度大约是 Nc

image

8、最大间距

164 - 最大间距

public class MaximumGap {

    public static int maximumGap(int[] nums) {
        if (nums.length < 2) return 0;

        int minV = Integer.MAX_VALUE;
        int maxV = -1;
        for (int num : nums) {
            minV = Math.min(minV, num);
            maxV = Math.max(maxV, num);
        }
        // 最大间距 >= minSpacing
        // 上取整, 最大间距的最小值 minSpacing = (maxV - minV) / (nums.length - 1)
        int minSpacing = (maxV - minV) / (nums.length - 1) + ((maxV - minV) % (nums.length - 1) != 0 ? 1 : 0);

        // 让桶内最大间距为 minSpacing, 这样可以保证最大间距的最大值不可能在桶内产生, 只能在桶间产生
        // 假设桶内元素的最小值为 x, 则桶内元素的取值范围为 [x ... x + minSpacing]
        int c = minSpacing + 1;                       // c 代表每个桶中的元素可能数
        int range = maxV - minV + 1;                  // 根据元素可能数决定桶的个数
        int B = range / c + (range % c != 0 ? 1 : 0); // B 个桶
        if (B == 1) return minSpacing;                // 如果只有 1 个桶, 直接返回 minSpacing
        int[] min = new int[B];
        int[] max = new int[B];
        Arrays.fill(min, Integer.MAX_VALUE);
        Arrays.fill(max, -1);
        for (int num : nums) {
            int p = (num - minV) / c;
            min[p] = Math.min(min[p], num);
            max[p] = Math.max(max[p], num);
        }

        int res = 0;
        int lastMax = max[0];
        for (int i = 1; i < B; i++) {
            if (min[i] == Integer.MAX_VALUE) continue; // 有的桶可能就没用到
            res = Math.max(res, min[i] - lastMax);
            lastMax = max[i];
        }

        return res;
    }

    public static void main(String[] args) {
        // int[] nums = {3, 6, 9, 1};
        int[] nums = {1, 1, 1, 1, 1, 1, 1, 2};
        System.out.println(maximumGap(nums));
    }
}
posted @ 2023-04-15 20:31  lidongdongdong~  阅读(15)  评论(0编辑  收藏  举报