24、桶排序
1、MSD 与 Bucket
2、桶排序原理
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、更简单的桶排序
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
8、最大间距
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));
}
}
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17321802.html