• 排序算法之计数排序



    一、简介

    算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度排序方式稳定性
    计数排序O(n+k )O(n+k)O(n+k)O(k)Out-place稳定

    稳定:如果A原本在B前面,而A=B,排序之后A仍然在B的前面;
    不稳定:如果A原本在B的前面,而A=B,排序之后A可能会出现在B的后面;
    时间复杂度: 描述一个算法执行所耗费的时间;
    空间复杂度:描述一个算法执行所需内存的大小;
    n:数据规模;
    k:“桶”的个数;
    In-place:占用常数内存,不占用额外内存;
    Out-place:占用额外内存。

    计数排序,又叫非比较排序。顾名思义,该算法不是通过比较数据的大小来进行排序的,而是通过统计数组中相同元素出现的次数,然后通过统计的结果将序列回收到原来的序列中。
    核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

    在这里插入图片描述

    算法步驟:

    (1)找出待排序的数组中最大和最小的元素
    (2)统计数组中每个值为i的元素出现的次数,存入数组C的第 i 项
    (3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
    (4)反向填充目标数组:将每个元素i放在新数组的第C( i )项,每放一个元素就将C( i )减去1


    二、代码实现

    public class CountingSort {
        public static void countingSort(int[] arr) {
            int len = arr.length;
            if (len < 2) return;
            int minVal = arr[0], maxVal = arr[0];
            for (int i = 1; i < len; i++) {
                if (arr[i] < minVal) {
                    minVal = arr[i];
                } else if (arr[i] > maxVal) {
                    maxVal = arr[i];
                }
            }
    
            int[] countArr = new int[maxVal - minVal + 1];
            for (int val : arr) {
                countArr[val - minVal]++;
            }
            for (int arrIdx = 0, countIdx = 0; countIdx < countArr.length; countIdx++) {
                while (countArr[countIdx]-- > 0) {
                    arr[arrIdx++] = minVal + countIdx;
                }
            }
        }
    
        public static void countingSort2(int[] arr) {
            int len = arr.length;
            if (len < 2) return;
            int minVal = arr[0], maxVal = arr[0];
            for (int i = 1; i < len; i++) {
                if (arr[i] < minVal) {
                    minVal = arr[i];
                } else if (arr[i] > maxVal) {
                    maxVal = arr[i];
                }
            }
    
            int[] countArr = new int[maxVal - minVal + 1];
            for (int val : arr) {
                countArr[val - minVal]++;
            }
            for (int countIdx = countArr.length - 1, arrIdx = 0; countIdx >= 0; countIdx--) {
                while (countArr[countIdx]-- > 0) {
                    arr[arrIdx++] = minVal + countIdx;
                }
            }
        }
    
        public static void main(String[] args) {
            int[] arr = {12, 11, 15, 50, 7, 65, 3, 99, 0};
            System.out.println("---排序前:  " + Arrays.toString(arr));
            countingSort(arr);
            System.out.println("计数排序从小到大:  " + Arrays.toString(arr));
            countingSort2(arr);
            System.out.println("计数排序从大到小:  " + Arrays.toString(arr));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    在这里插入图片描述

    三、应用场景

    • 适用于范围较小的整数排序:计数排序适用于对范围较小的整数进行排序,因为它的时间复杂度与输入数据的范围大小线性相关,而与数据规模无关。
    • 适用于重复值较多的情况:如果输入数据中存在大量重复值,计数排序可以有效地减少比较次数,提高排序效率。
    • 稳定性:计数排序是一种稳定的排序算法,可以保持相同元素的相对顺序不变。
    • 适用于非负整数排序:计数排序要求输入数据必须是非负整数,且适用于整数排序,不适用于字符串等其他类型的数据。
    • 适用于辅助排序算法:计数排序可以作为辅助排序算法,用于优化其他排序算法的性能,例如基数排序。

    参考链接:
    十大经典排序算法(Java实现)

  • 相关阅读:
    Chapter5.1:线性系统的频域分析法
    物联网运维-前端设备运维管理设计及解决方案
    Rust学习日记(一)Cargo的使用
    mock.js的使用
    什么是AIGC
    110道Java初级面试题及答案(最新Java初级面试题大汇总)
    PCB布线及后仿真验证过程(干货满满,建议收藏)
    python数据分析与展示--Matplotlib库入门
    K8S的控制器Deployment,ReplicaSet,StatefulSet,CronJob,最小单位pod
    【Python】:数据可视化之相关系数热力图绘制(二)(seaborn版本)
  • 原文地址:https://blog.csdn.net/qq_45649553/article/details/137929192