【算法】计数排序、桶排序、基数排序

算法系列八:非比较排序

一、计数排序

1.实现

1.1步骤

1.2代码

2.性质

2.1稳定性

2.1.1从前往后前始版:

2.1.2从后往前末始版:

2.2复杂度

2.2.1时间复杂度

2.2.2空间复杂度

二、桶排序

1.实现

1.1步骤

1.2代码

2.稳定性

三、基数排序

1.原理

2.代码


鸽巢原理

鸽子归巢,待排序数据归到有序组群中按大小归进有序组群来排数越大,归到的有序组就在越后的数越小,归到的有序组就在越前的

  • 如果有序组以一个元素一个元素划分 的,实现的是元素组归巢排序 ,即计数排序
  • 如果有序组按范围多个元素为一组划分 的,实现的是范围组归巢排序 ,即桶排序

一、计数排序

1.实现

1.1步骤

  1. 开辟数据范围内的所有元素都有各自对应的元素组巢穴 ,范围内一共有多少种个数据,就开辟每种个数据都有对应的多大的数组,比如90(max) - 10(min) = 80(种个数据)
  2. 归巢时,通过该数据-数据范围内的最小值****得到所归巢的下标 ,然后在数组元素巢中计数表示归巢
  3. 因为巢内只有一种个数据直接就是有序的,所以所有数据归完巢在**++巢层面++有序时所有数据就已经有序了** ,最后将它们按顺序地赶出巢加回去即得原来有序的数据

1.2代码

java 复制代码
    public static void countSort(int[] array) {
        //1.求当前数据的最大值和最小值
        int minVal = array[0];
        int maxVal = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i] < minVal) {
                minVal = array[i];
            }
            if(array[i] > maxVal) {
                maxVal = array[i];
            }
        }

        //2.根据数据最大值和最小值来确定元素组巢穴数组的大小
        int[] count = new int[maxVal-minVal+1];

        //3.遍历原来的数据进行归巢排序
        for (int i = 0; i < array.length; i++) {
            count[array[i]-minVal]++;
        }

        //4.将元素组巢穴里已排好序的数据按顺序写回array
        int index = 0;//重新表示array数组的下标
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                array[index] = i+minVal;
                index++;
                count[i]--;
            }
        }
    }
}

2.性质

2.1稳定性

每个数据都归到巢中完成有序时,根据巢中有序来的元素的计数个数 ,可以将巢改装成装每种个元素++有序排的始位置++,通过对应顺序遍历原数组将数据++正确稳定地放在排好序的各自位置上++,能实现稳定的排序,所以++计数排序是稳定的排序++

2.1.1从前往后前始版:

原本巢中装的是鸽子的计数数量 ,现在巢里面改装成装种个鸽子从前往后的起始位置 来进行排序:


2.1.2从后往前末始版:

巢里面改装成装种个鸽子从后往前的起始位置 来进行排序:


2.2复杂度

2.2.1时间复杂度

找最大最小值确定范围种个数据遍历原数组用了 n原数组数据每个去归巢用了n范围x种个元素巢每个去赶 ,所以时间复杂度为2n + x,即O(n+x)


2.2.2空间复杂度

范围x种个数据需要开辟x个元素巢的数组 ,所以空间复杂度为O(x)


二、桶排序

1.实现

1.1步骤

  1. 开辟 数据范围内所有元素都有对应的范围数组组巢穴将所有数据入范围组巢穴先进行第一轮巢穴外的排好序,此时巢外已经有序了
  2. 再进行第二轮各自巢穴内的排好序 ,此时就++巢外、巢内都有序所有数据都有序了++
  3. 最后按顺序地将它们从数组巢中赶出即得有序的数据

1.2代码

java 复制代码
    public static int[] bucketSort(int[] arr) {
        // 边界条件:空数组或单个元素直接返回
        if (arr.length <= 1) {
            return arr.clone();
        }

        // Step 1: 确定数据范围
        int minVal = Integer.MAX_VALUE;
        int maxVal = Integer.MIN_VALUE;
        for (int num : arr) {
            if (num < minVal) minVal = num;
            if (num > maxVal) maxVal = num;
        }

        // 处理所有元素相同的情况
        if (maxVal == minVal) {
            return arr.clone();
        }

        // Step 2: 初始化桶
        int bucketCount = (int) Math.sqrt(arr.length) + 1; // 桶数量=数组长度的平方根(经验值)
        double bucketRange = (double)(maxVal - minVal) / bucketCount;

        List<List<Integer>> buckets = new ArrayList<>(bucketCount);
        for (int i = 0; i < bucketCount; i++) {
            buckets.add(new ArrayList<>());
        }

        // Step 3: 元素分配到桶中
        for (int num : arr) {
            // 计算元素应该属于哪个桶
            int index = (int)((num - minVal) / bucketRange);
            // 处理最大值刚好落在最后一个桶外的情况
            if (index == bucketCount) index--;
            buckets.get(index).add(num);
        }

        // Step 4: 对每个桶内部排序
        for (List<Integer> bucket : buckets) {
            Collections.sort(bucket); // 使用内置排序算法,决定了桶排序的稳定性
        }

        // Step 5: 合并桶
        int[] sortedArr = new int[arr.length];
        int idx = 0;
        for (List<Integer> bucket : buckets) {
            for (int num : bucket) {
                sortedArr[idx++] = num;
            }
        }

        return sortedArr;
    }

2.稳定性

稳定性取决于在第二轮巢内开始排相同大小的元素时所用的排序方法是否具有稳定性


三、基数排序

1.原理

  1. 先进行个位排序 ,能实现++个位一位数有序++
  2. 个位有序下,再进行十位优先排序 ,能实现++十位个位两位数有序++
  3. 十个位有序下,再进行百位优先排序 ,能实现++百位十位个位三位数有序++

2.代码

java 复制代码
    public static int[] radixSort(int[] arr) {
        if (arr.length <= 1) {
            return arr.clone();
        }

        // Step 1: 确定最大数的位数
        int maxNum = Integer.MIN_VALUE;
        for (int num : arr) {
            if (num > maxNum) maxNum = num;
        }

        // Step 2: 按每位进行计数排序(从低位到高位)
        int exp = 1; // 从个位开始
        while (maxNum / exp > 0) {
            // 初始化10个数字桶(0-9)
            List<List<Integer>> buckets = new ArrayList<>(10);
            for (int i = 0; i < 10; i++) {
                buckets.add(new ArrayList<>());
            }

            // 按当前位分配到桶中
            for (int num : arr) {
                int digit = (num / exp) % 10; // 提取当前位的数字
                buckets.get(digit).add(num);
            }

            // 重组数组
            int idx = 0;
            for (List<Integer> bucket : buckets) {
                for (int num : bucket) {
                    arr[idx++] = num;
                }
            }

            exp *= 10; // 处理更高位
        }

        return arr;
    }
相关推荐
To_OC6 小时前
LC 131 分割回文串:刚学回溯时,我连怎么切字符串都想不明白
javascript·算法·leetcode
旖-旎7 小时前
LeetCode 518:零钱兑换||(完全背包)—— 题解
c++·算法·leetcode·动态规划·背包问题
To_OC8 小时前
LC 42 接雨水:暴力超时卡半天?前后缀数组一用就通了
javascript·算法·leetcode
delishcomcn9 小时前
AI视觉识别+分切算法:电化铝缺陷检测与裁切一体化解锁
人工智能·算法
触底反弹9 小时前
深入理解大模型采样:Temperature、Top-K、Top-P 的原理与实战
人工智能·算法·面试
雪碧聊技术9 小时前
力扣 LCR 091. 粉刷房子 —— 动态规划入门详解
算法·动态规划
延凡科技12 小时前
多场景落地复盘:端边云架构无人机智能巡检系统设计与实践
大数据·数据结构·人工智能·科技·架构·无人机·能源
CV-Climber12 小时前
检索技术的实际应用
人工智能·算法
hhzz13 小时前
Tiger AI Platform平台中增加人脸识别功能
图像处理·人工智能·算法·计算机视觉·大模型