桶排序的概念可以追溯到 20 世纪早期的穿孔卡片时代。在 1930 年代,IBM 的穿孔卡片排序机就使用了类似桶排序的原理。1956 年,E. J. Isaac 和 R. C. Singleton 在他们的论文中正式描述了桶排序。桶排序是分布排序的一种,它通过将数据分布到不同的"桶"中,然后对每个桶内的数据进行排序,最后按顺序合并所有桶来达到整体有序。
桶排序的基本思想:
桶排序的关键在于桶的划分。当数据均匀分布时,每个桶中的元素数量大致相同,此时桶排序的性能最佳。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BucketSort {
/**
* 桶排序(适用于 [0, 1) 区间内的浮点数)
*/
public static void sort(double[] arr) {
int n = arr.length;
if (n <= 1) return;
// 创建 n 个桶
@SuppressWarnings("unchecked")
List<Double>[] buckets = new ArrayList[n];
for (int i = 0; i < n; i++) {
buckets[i] = new ArrayList<>();
}
// 将元素分配到桶中
for (double value : arr) {
int bucketIndex = (int) (value * n);
if (bucketIndex >= n) bucketIndex = n - 1; // 处理边界情况
buckets[bucketIndex].add(value);
}
// 对每个桶进行排序
for (List<Double> bucket : buckets) {
Collections.sort(bucket);
}
// 合并所有桶
int index = 0;
for (List<Double> bucket : buckets) {
for (double value : bucket) {
arr[index++] = value;
}
}
}
/**
* 桶排序(适用于整数,指定范围)
* @param arr 待排序数组
* @param minValue 数组中的最小值
* @param maxValue 数组中的最大值
* @param bucketCount 桶的数量
*/
public static void sortIntegers(int[] arr, int minValue, int maxValue, int bucketCount) {
if (arr.length <= 1) return;
// 创建桶
@SuppressWarnings("unchecked")
List<Integer>[] buckets = new ArrayList[bucketCount];
for (int i = 0; i < bucketCount; i++) {
buckets[i] = new ArrayList<>();
}
// 计算桶的范围
double range = (double) (maxValue - minValue + 1) / bucketCount;
// 分配到桶
for (int value : arr) {
int bucketIndex = (int) ((value - minValue) / range);
if (bucketIndex >= bucketCount) bucketIndex = bucketCount - 1;
buckets[bucketIndex].add(value);
}
// 对每个桶进行排序
for (List<Integer> bucket : buckets) {
Collections.sort(bucket);
}
// 合并
int index = 0;
for (List<Integer> bucket : buckets) {
for (int value : bucket) {
arr[index++] = value;
}
}
}
/**
* 桶排序(泛型版本,使用自然排序)
*/
public static <T extends Comparable<T>> void sortGeneric(T[] arr, int bucketCount) {
if (arr.length <= 1) return;
// 找到最小值和最大值
T min = arr[0], max = arr[0];
for (T value : arr) {
if (value.compareTo(min) < 0) min = value;
if (value.compareTo(max) > 0) max = value;
}
// 创建桶
@SuppressWarnings("unchecked")
List<T>[] buckets = new ArrayList[bucketCount];
for (int i = 0; i < bucketCount; i++) {
buckets[i] = new ArrayList<>();
}
// 分配到桶
for (T value : arr) {
int bucketIndex = getBucketIndex(value, min, max, bucketCount);
buckets[bucketIndex].add(value);
}
// 排序每个桶
for (List<T> bucket : buckets) {
Collections.sort(bucket);
}
// 合并
int index = 0;
for (List<T> bucket : buckets) {
for (T value : bucket) {
arr[index++] = value;
}
}
}
private static <T extends Comparable<T>> int getBucketIndex(T value, T min, T max, int bucketCount) {
// 使用 double 进行比例计算
double ratio = (double) (value.compareTo(min)) / (max.compareTo(min) + 1);
int index = (int) (ratio * bucketCount);
if (index >= bucketCount) index = bucketCount - 1;
return index;
}
}
| 指标 | 最好情况 | 最坏情况 | 平均情况 |
|---|---|---|---|
| 时间复杂度 | O(n + k) | O(n²) | O(n + k) |
| 空间复杂度 | O(n + k) | O(n + k) | O(n + k) |
其中 k 是桶的数量。当数据均匀分布且桶内排序使用 O(1) 时间(每个桶只有一个元素)时,桶排序达到最优 O(n)。最坏情况是所有元素都落入同一个桶中,退化为 O(n²)。
桶排序是稳定的排序算法(取决于桶内排序算法的稳定性)。它是非比较排序算法,可以在 O(n) 时间内完成排序。