首页 > 编程语言 >【十大排序算法】计数排序

【十大排序算法】计数排序

时间:2024-06-13 20:30:26浏览次数:13  
标签:arr int 元素 计数 算法 数组 排序

数字在轻舞纷飞中,依次排列,如星辰般闪耀。

文章目录

一、计数排序

计数排序是一种非比较型的排序算法,它根据待排序元素的值来确定每个元素之前的有序位置。它的基本思想是统计待排序元素中每个元素出现的次数,然后根据这些统计信息,将元素放到正确的位置上。
具体来说,计数排序的步骤如下:

  1. 统计待排序数组中每个元素出现的次数,得到一个计数数组(count array)。
  2. 根据计数数组中的信息,确定每个元素在排序后的数组中的位置。
  3. 将待排序数组中的元素按照它们在计数数组中的位置依次放入到一个新的数组中,得到排好序的数组。

二、发展历史

计数排序是一种比较简单但非常有效的排序算法,其发展历史可以追溯到 20 世纪 50 年代。以下是计数排序的主要发展历史:

  1. 早期思想: 计数排序的思想可以追溯到早期的计算机科学研究。人们意识到,在某些情况下,可以通过统计每个元素的出现次数来对元素进行排序,而不是依赖比较操作。
  2. 基本概念确立: 计数排序的基本概念在 20 世纪 50 年代开始确立。在这个阶段,人们开始研究如何使用计数来排序元素,并且提出了一些基本的算法思想。
  3. 正式提出: 计数排序的正式提出可以追溯到 1964 年,由美国计算机科学家 Harold H. Seward 在他的论文 “Computer Algorithms for Sorting Files” 中首次提出。Seward 描述了计数排序的基本思想和算法步骤,并探讨了其在实际应用中的潜力。
  4. 进一步改进: 随着时间的推移,人们对计数排序进行了进一步的改进和优化。特别是在计算机科学领域的发展和算法研究中,出现了许多优化版本和变种,以提高计数排序的性能和适用性。
  5. 实际应用: 计数排序在实际应用中得到了广泛的应用。尤其是在一些特定的情况下,例如对一定范围内的整数进行排序时,计数排序具有很高的效率,并且在某些情况下甚至可以超过其他常见的排序算法,如快速排序和归并排序。

三、处理流程

场景假设:我们需要将下面的无序数组 A 使用计数排序按从小到大进行排序。
workspace (23).png
计数排序的流程如下:

  1. 找出待排序的数组中最大和最小的元素:在这个例子中,最小的元素是 1,最大的元素是 5。
  2. 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项:所以,我们得到 C = [0, 1, 1, 2, 2, 3]。这表示 1 出现了 1 次,2 出现了 1 次,3 出现了 2 次,4 出现了 2 次,5 出现了 3 次。
  3. 对所有的计数累加:我们更新 C 为 C = [0, 1, 2, 4, 6, 9]。这表示小于等于 1 的元素有 1 个,小于等于 2 的元素有 2 个,小于等于 3 的元素有 4 个,小于等于 4 的元素有 6 个,小于等于 5 的元素有 9 个。
  4. 反向填充目标数组:我们创建一个新的数组 B,大小和 A 相同。然后,我们遍历 A,对于 A 中的每个元素 i,我们都可以在 C 中找到小于等于 i 的元素数量,这就是 i 在 B 中的位置。然后,我们将 C[i] 减 1。

workspace (25).png

四、算法实现

public class CountingSort {
    void sort(int arr[]) {
        int n = arr.length;

        // 找出数组中的最大元素
        int max = arr[0];
        for (int i = 1; i < n; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }

        // 初始化计数数组,长度为最大元素加1
        int[] count = new int[max + 1];

        // 遍历原数组,统计每个元素的频率
        for (int i = 0; i < n; i++) {
            count[arr[i]]++;
        }

        // 对计数数组进行累加,使得 count[i] 表示小于等于 i 的元素数量
        for (int i = 1; i <= max; i++) {
            count[i] += count[i - 1];
        }

        // 创建一个新的数组,用于存放排序后的结果
        int[] output = new int[n];
        // 反向遍历原数组,根据计数数组中的信息,确定每个元素在排序后数组中的位置
        for (int i = n - 1; i >= 0; i--) {
            output[count[arr[i]] - 1] = arr[i];
            count[arr[i]]--;  // 每放置一个元素,相应的计数就减1
        }

        // 将排序后的结果复制回原数组
        for (int i = 0; i < n; i++) {
            arr[i] = output[i];
        }
    }

    public static void main(String args[]) {
        CountingSort cs = new CountingSort();
        int arr[] = {5, 4, 3, 2, 1, 5, 4, 3, 5};
        cs.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

算法时间复杂度分析:

情况时间复杂度计算公式公式解释
最好情况 O ( n + k ) O(n + k) O(n+k) T ( n ) = n + k T(n) = n + k T(n)=n+k n n n 是输入数组的长度, k k k 是输入数组中的最大值。这是因为我们需要遍历整个数组 ( n n n) 并对每个元素进行计数 ( k k k)。
平均情况 O ( n + k ) O(n + k) O(n+k) T ( n ) = n + k T(n) = n + k T(n)=n+k n n n 是输入数组的长度, k k k 是输入数组中的最大值。无论输入数组的实际内容如何,计数排序都需要遍历整个数组并对每个元素进行计数。
最坏情况 O ( n + k ) O(n + k) O(n+k) T ( n ) = n + k T(n) = n + k T(n)=n+k n n n 是输入数组的长度, k k k 是输入数组中的最大值。即使在最坏的情况下,计数排序的时间复杂度也是 O ( n + k ) O(n + k) O(n+k),因为它的运行时间主要取决于输入数组的长度和最大值。

五、算法特性

计数排序(Counting Sort)是一种非比较排序算法,其特性包括:

  1. 稳定性: 计数排序是一种稳定的排序算法。稳定性是指对于具有相同排序键值的元素,排序前后它们的相对位置保持不变。计数排序在统计元素出现次数并进行排序时,保持了相同元素的相对顺序不变。
  2. 原地性: 计数排序并不是一个原地排序算法。
  3. 适用性: 计数排序适用于一定范围内的整数排序,特别是对于取值范围不大的情况。因为它不依赖于元素间的比较,而是统计每个元素出现的次数,然后根据这些统计信息确定元素的位置。
  4. 空间复杂度: 计数排序的空间复杂度较高,为 O ( n + k ) O(n + k) O(n+k),其中 n n n 是待排序元素的个数, k k k 是待排序元素中的最大值与最小值之差加 1 1 1。因为计数排序需要额外的空间来存储元素出现的次数以及辅助数组来保存排序结果。
  5. 线性时间复杂度: 计数排序的时间复杂度为 O ( n + k ) O(n + k) O(n+k),其中 n n n 是待排序元素的个数, k k k 是待排序元素中的最大值与最小值之差加 1 1 1。这意味着计数排序的时间复杂度与待排序数据的范围大小相关,而与待排序数据的数量无关。
  6. 不适用于负数和浮点数: 计数排序通常用于排序非负整数,因为它要求元素必须是非负整数,并且需要知道待排序数据的范围。对于负数和浮点数,需要进行一些额外的处理才能适应计数排序的要求。
  7. 不稳定范围扩展: 在一些情况下,计数排序的稳定性可能受到范围扩展的影响。如果对于相同的键值,在不同的范围内进行计数排序,可能会导致稳定性受到破坏。

六、小结

计数排序是一种简单而高效的排序算法,尤其适用于范围有限且重复元素较多的情况。通过统计每个元素的频率并根据累计频率将元素放置到正确位置上,可以在线性时间内完成排序。然而,需要注意的是,计数排序的空间复杂度较高,因为需要额外的空间来存储频率数组。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

标签:arr,int,元素,计数,算法,数组,排序
From: https://blog.csdn.net/LearnerDL/article/details/139581491

相关文章

  • Python简单实现:读取文件夹并数字排序
    python中os.listdir()方法用于返回指定的文件夹包含的文件或文件夹的名字的列表importospath="../data/materials/test/"path_list=os.listdir(path)print(path_list)输出['1.jpg','10.jpg','11.jpg','12.jpg','13.jpg',......
  • m基于PSO-GRU粒子群优化长门控循环单元网络的电力负荷数据预测算法matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下: 优化前:    优化后:    对比如下:   2.算法涉及理论知识概要       基于粒子群优化(ParticleSwarmOptimization,PSO)和长门控循环单元(GatedRecurrentUnit,GRU)网络的电力负荷预测算法,是一种融合......
  • 数据结构与算法1 简要复习
    1.三种复杂度Ο,读音:big-oh;表示上界,小于等于。Ω,读音:bigomega、欧米伽;表示下界,大于等于。Θ,读音:theta、西塔;既是上界也是下界,称为确界,等于。2.抽象数据类型3.堆,栈(queue,stack)4.哈希线性探测二次探测(重要)二次哈希5.二叉搜索树(BST)#include<iostream>//定义二叉搜索......
  • 隐语课程学习笔记5-隐私保护机器学习算法概要
    隐语课程第5课,简单介绍了隐语的算法能力,包括预处理、隐私求交、决策树模型、线性回归模型、神经网络模型,并且支持数据水平切分(横向联邦)、垂直切分(纵向联邦)、混合切分(横纵向联邦)。隐语提供了包括对DataFrame的封装,以及提供联邦ndarray的封装,和python的使用基本一致,上手较快,比......
  • C#中List的3种排序方法
    原文链接:https://blog.csdn.net/lwf3115841/article/details/130459042         https://blog.csdn.net/Pei_hua100/article/details/108072643ist是C#常用的数组,它较之前的ArryList更加灵活,解决了Arrylist会出现装箱和拆箱的不安全问题,它是一种动态数组,可以存......
  • 第二届算法、图像处理与机器视觉国际学术会议(AIPMV2024)
    第二届算法、图像处理与机器视觉国际学术会议(AIPMV2024)20242ndInternationalConferenceonAlgorithm,ImageProcessingandMachineVision(AIPMV2024)2024年7月12日-14日江苏镇江大会官网:https://ais.cn/u/jyUbAz【更多内容】主办单位:江苏大学、中国图象图形学会承办单......
  • C++的算法:割点与割边
            在图论中,割点与割边是图的重要性质,它们在图的连通性、网络流等问题中扮演着关键角色。在C++中,我们可以通过深度优先搜索(DFS)等算法来判定一个图中的割点与割边。        割点,又称关节点或桥接点,是指在无向连通图中,如果删除某个顶点后,图的连通分量数增......
  • 【SPIE独立出版、有ISBN号和ISSN号、往届均已实现EI检索】第四届计算机视觉、应用与算
    第四届计算机视觉、应用与算法国际学术会议(CVAA2024)The4thInternationalConferenceonComputerVision, ApplicationandAlgorithm(CVAA2024)一、重要信息会议官网:www.iccvaa.org (点击投稿/参会/了解会议详情)会议时间:2024年8月30日-9月1日会议地点:中国-杭州截......
  • C语言题目:排序问题2
    题目描述将十个数进行从大到小的顺序进行排列输入格式十个整数输出格式以从大到小的顺序输出这个十个数样例输入12345678910样例输出10987654321代码解析1.引入头文件代码首先引入了stdio.h头文件,这是C语言标准输入输出库,用于处理输入输出......
  • 多源最短路径算法 -- 弗洛伊德(Floyd)算法
    1. 简介        Floyd算法,全名为Floyd-Warshall算法,亦称弗洛伊德算法或佛洛依德算法,是一种用于寻找给定加权图中所有顶点对之间的最短路径的算法。这种算法以1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德的名字命名。2.核心思想    ......