rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 桶排序

  • 算法定义
  • 算法思路
  • 算法复杂度
  • 优点与缺点
  • 应用场景
  • 各版本优化说明
  • 代码
  • 代码2
  • 资料

桶排序

算法定义

桶排序(Bucket Sort)是一种分布式排序算法,它将待排序数据分配到多个有序的 "桶" 中,对每个桶内的元素单独排序(通常使用其他排序算法),最后将所有桶中的元素按顺序合并,得到最终的有序数组。

桶排序的核心思想是利用数据的分布特性,通过映射函数将数据均匀分配到不同桶中,从而减少单个桶内的排序开销。

算法思路

桶排序的步骤如下:

  1. 创建桶:根据数据范围和分布,创建一定数量的空桶(通常是数组或链表)。
  2. 分配元素:遍历待排序数组,根据映射函数将每个元素放入对应的桶中。
  3. 桶内排序:对每个非空桶内的元素进行排序(可使用插入排序、快速排序等)。
  4. 合并结果:按桶的顺序依次将所有桶中的元素取出,拼接成有序数组。

例如,排序[0.897, 0.565, 0.656, 0.1234, 0.665, 0.3434]:

  • 创建 10 个桶(索引 0-9)
  • 按num * 10的整数部分分配:0.897→8 号桶,0.565→5 号桶等
  • 对每个桶内元素排序后合并:[0.1234, 0.3434, 0.565, 0.656, 0.665, 0.897]

算法复杂度

  • 最好时间复杂度:O (n + k) - n 为元素总数,k 为桶数,当数据均匀分布且桶内无需排序时
  • 最坏时间复杂度:O (n²) - 所有元素落入同一桶,且使用 O (n²) 排序算法时
  • 平均时间复杂度:O (n + k + n log (n/k)) - 假设数据均匀分布,每个桶有 n/k 个元素
  • 空间复杂度:O (n + k) - 需要存储 n 个元素和 k 个桶

优点与缺点

  • 优点:
    • 当数据分布均匀时,效率接近线性时间 O (n)
    • 可并行化处理(每个桶可独立排序)
    • 适合处理大规模数据
    • 对浮点数排序友好
  • 缺点:
    • 对数据分布敏感,分布不均时效率大幅下降
    • 需要额外空间存储桶
    • 不适合处理值域范围极大的数据
    • 实现较复杂,需设计合适的映射函数
    • 桶排序的复杂度是值域相关的。当值域很大并且分布不均匀时,桶排序需要增加轮数,效率随之降低。
    • 桶排序不是比较排序,对于一些需要比较才能完成排序的问题(如精确的分数比较),桶排序就不能实现。

所以这会有由桶排序所进化成的基数排序

应用场景

  • 数据分布均匀的场景(如成绩、身高、体重等)
  • 浮点数排序(如 0-1 区间的小数)
  • 大规模数据的外部排序(结合磁盘存储)
  • 数据库系统中的范围查询优化
  • 哈希表中的数据排序

各版本优化说明

  1. 基本版(浮点数):
    • 处理 0-1 区间的浮点数,映射函数简单(num * n)
    • 使用 ArrayList 作为桶,实现简单直观
    • 适合演示桶排序的基本原理
  2. 整数优化版:
    • 自动计算数据范围,动态确定桶数量和每个桶的范围
    • 对小规模桶使用插入排序,大规模桶使用快速排序
    • 桶数量通常设为√n,平衡时间和空间效率
  3. 链表桶优化:
    • 使用 LinkedList 代替 ArrayList 作为桶,减少动态扩容开销
    • 适合数据分布不均匀、桶大小波动大的场景
    • 利用链表的特性优化插入操作
  4. 并行桶排序:
    • 按 CPU 核心数设置桶数量,支持多线程并行排序
    • 大幅提升大规模数据的排序效率
    • 适合多核处理器环境,充分利用硬件资源

桶排序是一种高效的分布式排序算法,其性能高度依赖数据分布。在实际应用中,合理设计映射函数和选择桶内排序算法是发挥其优势的关键。当数据均匀分布时,桶排序可以达到接近线性的时间复杂度,是处理大规模数据的理想选择。

代码

import java.util.*;

@SuppressWarnings("unchecked") // 抑制泛型数组相关的未检查警告
public class BucketSort {
    
    // 1. 基本版桶排序(处理0-1区间的浮点数)
    public static void bucketSortBasic(double[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        int n = arr.length;
        // 创建n个桶,添加注解抑制警告
        List<Double>[] buckets = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            buckets[i] = new ArrayList<>();
        }
        
        // 将元素分配到桶中
        for (double num : arr) {
            int bucketIndex = (int) (num * n); // 映射函数:num * n
            buckets[bucketIndex].add(num);
        }
        
        // 对每个桶排序并合并结果
        int index = 0;
        for (List<Double> bucket : buckets) {
            if (!bucket.isEmpty()) {
                Collections.sort(bucket);
                for (double num : bucket) {
                    arr[index++] = num;
                }
            }
        }
    }
    
    // 2. 优化版1:处理整数的桶排序
    public static void bucketSortIntegers(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        // 找出最大值和最小值,确定桶的范围
        int min = arr[0], max = arr[0];
        for (int num : arr) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        // 计算桶的数量和每个桶的范围
        int bucketCount = (int) Math.sqrt(arr.length) + 1;
        int range = (max - min) / bucketCount + 1;
        
        // 创建桶
        List<Integer>[] buckets = new ArrayList[bucketCount];
        for (int i = 0; i < bucketCount; i++) {
            buckets[i] = new ArrayList<>();
        }
        
        // 分配元素到桶
        for (int num : arr) {
            int bucketIndex = (num - min) / range;
            buckets[bucketIndex].add(num);
        }
        
        // 桶内排序并合并
        int index = 0;
        for (List<Integer> bucket : buckets) {
            if (!bucket.isEmpty()) {
                if (bucket.size() <= 15) {
                    insertionSort(bucket);
                } else {
                    Collections.sort(bucket);
                }
                for (int num : bucket) {
                    arr[index++] = num;
                }
            }
        }
    }
    
    // 3. 优化版2:使用链表桶和自定义排序(减少扩容开销)
    public static void bucketSortLinkedList(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        int min = arr[0], max = arr[0];
        for (int num : arr) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        // 修复1:合理计算桶数量(参考其他优化版)
        int bucketCount = (int) Math.sqrt(arr.length) + 1;
        // 修复2:计算每个桶的范围,避免索引越界
        int range = (max - min) / bucketCount + 1;
        
        // 使用链表作为桶
        LinkedList<Integer>[] buckets = new LinkedList[bucketCount];
        for (int i = 0; i < bucketCount; i++) {
            buckets[i] = new LinkedList<>();
        }
        
        // 分配元素(修复3:使用range计算索引)
        for (int num : arr) {
            int bucketIndex = (num - min) / range;
            buckets[bucketIndex].add(num);
        }
        
        // 合并结果
        int index = 0;
        for (LinkedList<Integer> bucket : buckets) {
            if (!bucket.isEmpty()) {
                bucket.sort(null);
                for (int num : bucket) {
                    arr[index++] = num;
                }
            }
        }
    }
    
    // 4. 优化版3:并行桶排序(多线程处理)
    public static void bucketSortParallel(int[] arr) throws InterruptedException {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        int min = arr[0], max = arr[0];
        for (int num : arr) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        int bucketCount = Runtime.getRuntime().availableProcessors();
        int range = (max - min) / bucketCount + 1;
        
        List<Integer>[] buckets = new ArrayList[bucketCount];
        for (int i = 0; i < bucketCount; i++) {
            buckets[i] = new ArrayList<>();
        }
        
        // 分配元素
        for (int num : arr) {
            int bucketIndex = (num - min) / range;
            buckets[bucketIndex].add(num);
        }
        
        // 多线程并行排序
        Thread[] threads = new Thread[bucketCount];
        for (int i = 0; i < bucketCount; i++) {
            final int bucketIdx = i;
            threads[i] = new Thread(() -> {
                if (!buckets[bucketIdx].isEmpty()) {
                    Collections.sort(buckets[bucketIdx]);
                }
            });
            threads[i].start();
        }
        
        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }
        
        // 合并结果
        int index = 0;
        for (List<Integer> bucket : buckets) {
            for (int num : bucket) {
                arr[index++] = num;
            }
        }
    }
    
    // 辅助方法:对List进行插入排序
    private static void insertionSort(List<Integer> list) {
        for (int i = 1; i < list.size(); i++) {
            int key = list.get(i);
            int j = i - 1;
            while (j >= 0 && list.get(j) > key) {
                list.set(j + 1, list.get(j));
                j--;
            }
            list.set(j + 1, key);
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        double[] arr1 = {0.897, 0.565, 0.656, 0.1234, 0.665, 0.3434};
        int[] arr2 = {43, 32, 22, 78, 63, 57, 91, 13, 25, 87};
        int[] arr3 = {43, 32, 22, 78, 63, 57, 91, 13, 25, 87};
        int[] arr4 = {43, 32, 22, 78, 63, 57, 91, 13, 25, 87};
        
        bucketSortBasic(arr1);
        System.out.println("基本版(浮点数): " + Arrays.toString(arr1));
        
        bucketSortIntegers(arr2);
        System.out.println("整数优化版: " + Arrays.toString(arr2));
        
        bucketSortLinkedList(arr3);
        System.out.println("链表桶优化: " + Arrays.toString(arr3));
        
        bucketSortParallel(arr4);
        System.out.println("并行桶排序: " + Arrays.toString(arr4));
    }
}

代码2

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

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

    public static void main(String[] args) {

        int[] nums = SortUtils.arrays();
        bucketSortExam(nums);
        System.out.println("桶排序:" + Arrays.toString(nums));
    }

    private static void bucketSort(int[] nums) {
        int INTERVAL = 100;               // 定义桶的大小
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int num : nums) {            // 找到数组元素的范围
            min = Math.min(min, num);
            max = Math.max(max, num);
        }
        int count = (max - min + 1);      // 计算出桶的数量
        int bucketSize = (count % INTERVAL == 0) ? (count / INTERVAL) : (count / INTERVAL + 1);
        List<Integer>[] buckets = new List[bucketSize];
        for (int num : nums) {            // 把所有元素放入对应的桶里面
            int quotient = (num - min) / INTERVAL;
            if (buckets[quotient] == null) buckets[quotient] = new ArrayList<>();
            buckets[quotient].add(num);
        }
        int cur = 0;
        for (List<Integer> bucket : buckets) {
            if (bucket != null) {
                bucket.sort(null);       // 对每个桶进行排序
                for (Integer integer : bucket) {  // 还原桶里面的元素到原数组
                    nums[cur++] = integer;
                }
            }
        }
    }

    public static void bucketSort2(int[] arr) {
        //最大最小值
        int max = arr[0];
        int min = arr[0];
        int length = arr.length;

        for (int i = 1; i < length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            } else if (arr[i] < min) {
                min = arr[i];
            }
        }

        //桶列表
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
        for (int i = 0; i < length; i++) {
            bucketList.add(new ArrayList<>());
        }

        //每个桶的存数区间
        float section = (float) (max - min) / (float) (length - 1);

        //数据入桶
        for (int i = 0; i < length; i++) {
            //当前数除以区间得出存放桶的位置 减1后得出桶的下标
            int num = (int) (arr[i] / section) - 1;
            if (num < 0) {
                num = 0;
            }
            bucketList.get(num).add(arr[i]);
        }

        //桶内排序
        for (int i = 0; i < bucketList.size(); i++) {
            //jdk的排序速度当然信得过
            Collections.sort(bucketList.get(i));
        }

        //写入原数组
        int index = 0;
        for (ArrayList<Integer> arrayList : bucketList) {
            for (int value : arrayList) {
                arr[index] = value;
                index++;
            }
        }
    }

    // https://www.cxyxiaowu.com/587.html
    // 待处理
    int min = Integer.MAX_VALUE;
    int max = Integer.MIN_VALUE;

    public int maximumGap(int[] nums) {
        if (nums == null || nums.length < 2) {
            return 0;
        }

        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int i : nums) {
            min = Math.min(min, i);
            max = Math.max(max, i);
        }

        // 分配桶的长度和个数是桶排序的关键
        // 在 n 个数下,形成的两两相邻区间是 n - 1 个,比如 [2,4,6,8] 这里
        // 有 4 个数,但是只有 3 个区间,[2,4], [4,6], [6,8]
        // 因此,桶长度 = 区间总长度 / 区间总个数 = (max - min) / (nums.length - 1)
        int bucketSize = Math.max(1, (max - min) / (nums.length - 1));

        // 上面得到了桶的长度,我们就可以以此来确定桶的个数
        // 桶个数 = 区间长度 / 桶长度
        // 这里考虑到实现的方便,多加了一个桶,为什么?
        // 还是举上面的例子,[2,4,6,8], 桶的长度 = (8 - 2) / (4 - 1) = 2
        //                           桶的个数 = (8 - 2) / 2 = 3
        // 已知一个元素,需要定位到桶的时候,一般是 (当前元素 - 最小值) / 桶长度
        // 这里其实利用了整数除不尽向下取整的性质
        // 但是上面的例子,如果当前元素是 8 的话 (8 - 2) / 2 = 3,对应到 3 号桶
        //              如果当前元素是 2 的话 (2 - 2) / 2 = 0,对应到 0 号桶
        // 你会发现我们有 0,1,2,3 号桶,实际用到的桶是 4 个,而不是 3 个
        // 透过例子应该很好理解,但是如果要说根本原因,其实是开闭区间的问题
        // 这里其实 0,1,2 号桶对应的区间是 [2,4),[4,6),[6,8)
        // 那 8 怎么办?多加一个桶呗,3 号桶对应区间 [8,10)
        Bucket[] buckets = new Bucket[(max - min) / bucketSize + 1];

        for (int i = 0; i < nums.length; ++i) {
            int loc = (nums[i] - min) / bucketSize;

            if (buckets[loc] == null) {
                buckets[loc] = new Bucket();
            }

            buckets[loc].min = Math.min(buckets[loc].min, nums[i]);
            buckets[loc].max = Math.max(buckets[loc].max, nums[i]);
        }

        int previousMax = Integer.MAX_VALUE;
        int maxGap = Integer.MIN_VALUE;
        for (int i = 0; i < buckets.length; ++i) {
            if (buckets[i] != null && previousMax != Integer.MAX_VALUE) {
                maxGap = Math.max(maxGap, buckets[i].min - previousMax);
            }

            if (buckets[i] != null) {
                previousMax = buckets[i].max;
                maxGap = Math.max(maxGap, buckets[i].max - buckets[i].min);
            }
        }

        return maxGap;
    }


    // ===练习===
    public static void bucketSortExam(int[] nums) {

        int INTERVAL = 100;
        int min = Integer.MIN_VALUE;
        int max = Integer.MAX_VALUE;
        for (int num : nums) {
            min = Math.min(num, min);
            max = Math.max(num, max);
        }
        // 计算桶数
        int count = max - min + 1;
        int bucketSize = count / INTERVAL == 0 ? count / INTERVAL : count / INTERVAL + 1;
        List<Integer>[] buckets = new List[bucketSize];
        for (int num : nums) {
            int quotient = (num - min) / INTERVAL;
            if (buckets[quotient] == null) buckets[quotient] = new ArrayList<>();
            buckets[quotient].add(num);
        }

        int cur = 0;
        for (List<Integer> bucket : buckets) {
            if (bucket != null) {

                // 对每个桶进行排序
                bucket.sort(null);
                // 还原桶里面的元素到原数组
                for (Integer integer : bucket) {
                    nums[cur++] = integer;
                }
            }
        }
    }
}

资料

https://www.cxyxiaowu.com/587.html

最近更新:: 2025/9/8 23:27
Contributors: luokaiwen