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

算法系列八:非比较排序

一、计数排序

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;
    }
相关推荐
-qOVOp-10 分钟前
408第一季 - 数据结构 - 图II
数据结构
凌辰揽月10 分钟前
Web后端基础(基础知识)
java·开发语言·前端·数据库·学习·算法
-qOVOp-10 分钟前
408第一季 - 数据结构 - 树与二叉树III
数据结构
lifallen16 分钟前
深入浅出 Arrays.sort(DualPivotQuicksort):如何结合快排、归并、堆排序和插入排序
java·开发语言·数据结构·算法·排序算法
jingfeng51417 分钟前
数据结构排序
数据结构·算法·排序算法
能工智人小辰42 分钟前
Codeforces Round 509 (Div. 2) C. Coffee Break
c语言·c++·算法
kingmax5421200843 分钟前
CCF GESP202503 Grade4-B4263 [GESP202503 四级] 荒地开垦
数据结构·算法
岁忧1 小时前
LeetCode 高频 SQL 50 题(基础版)之 【高级字符串函数 / 正则表达式 / 子句】· 上
sql·算法·leetcode
eachin_z2 小时前
力扣刷题(第四十九天)
算法·leetcode·职场和发展
闻缺陷则喜何志丹2 小时前
【强连通分量 缩点 拓扑排序】P3387 【模板】缩点|普及+
c++·算法·拓扑排序·洛谷·强连通分量·缩点