5.1 桶排序:空间换时间的策略

历史背景

桶排序的概念可以追溯到 20 世纪早期的穿孔卡片时代。在 1930 年代,IBM 的穿孔卡片排序机就使用了类似桶排序的原理。1956 年,E. J. Isaac 和 R. C. Singleton 在他们的论文中正式描述了桶排序。桶排序是分布排序的一种,它通过将数据分布到不同的"桶"中,然后对每个桶内的数据进行排序,最后按顺序合并所有桶来达到整体有序。

算法原理

桶排序的基本思想:

  1. 创建若干个桶(通常是固定数量)
  2. 遍历输入数据,将每个元素放入对应的桶中
  3. 对每个非空桶内的数据进行排序(可以使用任何排序算法)
  4. 按桶的顺序依次取出所有元素,得到有序序列

桶排序的关键在于桶的划分。当数据均匀分布时,每个桶中的元素数量大致相同,此时桶排序的性能最佳。

Java 实现

java
复制代码
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) 时间内完成排序。

应用场景

  • 数据均匀分布的场景
  • 浮点数排序
  • 数据范围已知且有限的场景
  • 分布式排序(不同桶可以在不同机器上处理)
  • 外部排序的预处理