计数排序的解析看完直接明白(课内学习笔记四)

news/2025/3/16 1:23:37/文章来源:https://www.cnblogs.com/cai-cai-zi/p/18774465

最近完成了分治章节的学习,包括分治求解数组子段和,分治法求解最大值,堆排,快排。今天又遇见了计数排序,这里使用了前缀和和一些小技巧,我觉得会经常用到,所以写下这篇文章,记录一下今天在算法方面的学习记录

计数排序的原理和步骤

计数排序适合于元素是整数的数组,小数的话是无法去建立计数数组下标与数组元素的对应关系(不理解的话可以继续看),负数的话也是支持的
这里需要四次遍历,每一次遍历都有自己的意义,让我们继续分析

第一次遍历:获取最大值和最小值

我们假设需要排序的数组是arr = <6,0,2,0,1,3,4,6,1,3,2> 共11个数
遍历求最小值和最大值的方法应该不用说了吧,初始化的时候可以拿第一个数作为min或者max(在没有INT_MIN和INT_MAX的情况下)
获取最大值max=6,最小值为min=0,我们需要创建max-min+1个元素的数组arr2
这是为什么呢?实际上我们需要一个连续排列的从0到6的数组,6-0总共有7个数,所以要求连续的值的个数时是max-min+1
这个数组包括从min到max的所有整数,数组中不一定全都有,比如arr就没有5这个元素

有了这个数组后我们可以进行下一步

第二次遍历:获得计数数组

我们在这次遍历要统计每一个元素的出现次数,比如6就出现了2次,0出现了2次
保存在哪里呢?我们在第一次中有一个七个元素的数组arr2,我们将min元素的次数保存在下标为0的地方,max元素的次数保存在下标为max-min+1这里是7的地方
min的下标是0,min+1的下标是1.... 直到max的下标是max-min

由于这里的arr的min为0,所以和新数组的下标重合了,我们再举个例子,假如min是3,max是8,max-min+1就是6
对应于arr中min到max: 3,4,5,6,7,8
arr2: 0,1,2,3,4,5
3(min)+0(arr2下标) = arr中的min
3(min)+1(arr2下标) = arr中的min+1 (可能存在也可能不存在,这只是范围中可以出现的值)

这里就有对应关系 arr2的下标+min 对应 arr数组区间内可以存在的min到max元素值,这里和求连续值个数可以一起当作一个技巧
然后就要在这个数组保存个数,最后arr2=<2,2,2,2,1,0,2>
意思是arr2下标为0的元素,也就代表arr为0的元素的个数是2,同理其他的

有了计数数组的话,我们可以继续下一步

第三次遍历:获得前缀和

前缀和的概念是一个连续求和的数
pre[0] = arr2[0]
pre[1] = arr2[0] + arr2[1]
pre[2] = arr2[0] + arr2[1] + arr2[2]
pre[n] = arr2[0] + ... arr[n]
但是我们可以发现pre[0]实际上已经把arr2[0]计算出来了,pre[1]已经把arr2[0]+arr2[1]计算出来了
pre[1] = pre[0] + arr2[1]
pre[2] = pre[1] + arr2[2]
...
这样子就有新的递归式 pre[n] = pre[n-1] + arr2[n](n>=1); pre[0] = arr2[0]
我们根据arr2求得的pre为
我们求得pre[7] = <2,4,6,8,9,9,11>

这个有什么意义呢?
pre[0] = 2意思是 arr2下标为0的元素包括自己,假如在11个数排序的时候,在它前面的有几个数(包括自己)
arr2下标为0的元素代表着arr中的min,也就是说 arr中的0这个元素 ,在它前面有包括自己应该有两个值
0是arr中最小的值,且有两个,所以这个2代表的是0在排序后最后一个'0'元素的应该处的位置,也就说第二个'0'的位置

2是代表是下标为2吗?也不是。
数组是从0开始的,0,1,2从0开始为第一个值的话,第二个值的下标就为1,所以2-1=1才是数组排序后真正的坐标
0是第一个'0'的真正的坐标

总结一下就是pre[n] 实际上是在arr中,n+min这个元素,排序后在它前面的有几个数(包括自己)

怎么排序呢?
拿arr中的2举例子,2有两个分别在arr下标为2,10的位置
当我们遍历arr到下标为2的值'2'时,我们记得哪个对应关系吗, arr2中下标+min=arr中代表的元素
我们交换一下 arr2下标 = arr元素 - min
arr2下标 = 2 - 0 = 2 arr2的下标和pre的下标是一样的
pre2[2] = 6 也就是说,第二个'2',在排序后应该处的位置是 6-1 = 5
我们拿排序的数组好的举个例子
数组 0 0 1 1 2 2 3 3 4 6 6
下标 0 1 2 3 4 5 6 7 8 9 10

第二个怎么排序呢?
我们要把pre[2] = 6 要-1=5 ,因为第二个2已经排完了,除了第二个2之外,第一个2包括自己,在它之前还有5个值
所以在arr[10]遇到2时,下标就是pre[2]-1 = 5 - 1 = 4
第二个放到下标为4的位置

第四次遍历

这样就很明白了,我们现在要遍历arr,然后再从pre中找到这个arr中元素应该处在的位置,如果有相同元素的话,是最后一个元素处的位置。
为了保持序列排序稳定,从后往前遍历,最后一个2先遍历就应该放在最后一个2位置上

分析完结,给出C++代码

C++代码

#include <iostream>
#include <vector>
#include <algorithm>void countingSort(std::vector<int>& arr) {if (arr.empty()) return;// 找出数组中的最大值和最小值int max = *std::max_element(arr.begin(), arr.end());int min = *std::min_element(arr.begin(), arr.end());// 计算计数数组的长度int range = max - min + 1;// 初始化计数数组std::vector<int> count(range, 0);// 统计每个元素的出现次数for (int num : arr) {// 通过 num - min 计算出在计数数组中的索引count[num - min]++;}// 计算前缀和for (int i = 1; i < range; i++) {count[i] += count[i - 1];}// 初始化输出数组std::vector<int> output(arr.size());// 填充输出数组for (int i = arr.size() - 1; i >= 0; i--) {output[count[arr[i] - min] - 1] = arr[i];count[arr[i] - min]--;}// 将排序结果复制回原数组for (int i = 0; i < arr.size(); i++) {arr[i] = output[i];}
}int main() {std::vector<int> arr = {3, 5, 7};countingSort(arr);std::cout << "Sorted array: ";for (int num : arr) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/899491.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三分钟教学:手把手教你实现Arduino发布第三方库

Arduino 发布第三方库的流程包括:构建库的基本框架后将其打包并上传至 GitHub,在 GitHub 上创建 Tag 和 Release 后,提交到 Arduino 库管理器,最后在Arduino IDE进行验证。三分钟教学:手把手教你实现Arduino发布第三方库原文链接: 手把手教你实现Arduino发布第三方库 摘要…

2025-315晚会总结

🔖简介 2025年315晚会曝光了多个行业的消费乱象和违法侵权行为。 主题:“共铸诚信 提振消费”,聚焦食品安全、公共安全、金融安全、数字经济等领域。 核心诉求:打击消费陷阱,推动构建公平、诚信的消费环境。 📢曝光现象 🔒数据安全与隐私侵权非法窃取个人信息涉事企业…

再破难关(BFS)

问题 F: 再破难关 题目描述 OIBH组织派出的黄金十二人+青铜五小强还没有到, 他们只能指望原先的机关能够阻拦住柯南的脚步。柯南打开大门之后发现里面还有一个门, 门上还有一个神奇的锁(-,-) 这是一个4*4的锁, 上面有8个凸起的格子和8个被按下的格子,当且仅当两个格子有公共边…

LLM大模型:OpenManus原理

继deepseek之后,武汉一个开发monica的团队又开发了manus,号称是全球第一个通用的agent!各路自媒体企图复刻下一个deepseek,疯狂报道!然而manus发布后不久,metaGPT团队5个工程师号称耗时3小时就搞定了一个demo版本的manus,取名openManus,才几天时间就收获了34.4K的start…

Day14_TCP三次握手

每日一题 TCP三次握手详解 三次握手(Three-Way Handshake) 是TCP协议建立可靠连接的核心过程,确保通信双方能够正常收发数据并同步初始序列号。以下是详细步骤和原理:1. 第一次握手:SYN(客户端 → 服务器)动作:客户端发送一个TCP报文,设置SYN=1(同步标志位),并生成…

Paimon merge into 实现原理

语法 MERGE INTO target USING source ON source.a = target.a WHEN MATCHED THEN UPDATE SET a = source.a, b = source.b, c = source.c WHEN NOT MATCHED THEN INSERT (a, b, c) values (a, b, c)merge into 实际上是一个语法糖, 相对应的语义也可以通过其他的 sql…

MACD

目录背景和价值用法快线在0轴上方 - 多头较强,否则多头较弱快线上穿慢线 形成金叉,形成多头信号。 快线下穿慢线 形成死叉,形成空头信号顶背离和底背离参考资料 背景和价值 指数平滑移动平均线两个(12和26)均线相交,12EMA上穿26EMA形成金叉,快线从0轴下方上穿0轴上方 用…

python 文件打包成 whl

首先需要安装 wheel, setuptools pip install setuptools wheel简单进行一个打包的例子,项目目录结构如下:# __init__.py def pytest_collection_modifyitems(session, config, items):for item in items:# item.name 用例名称item.name = item.name.encode(utf-8).decode(un…

cmake识别不到vcpkg安装的包的解决(以libssh为例)

承接上篇 vcpkg 跨平台的c/c++库包管理工具(以libssh为例) - 夕西行 - 博客园 vcpkg安装libssh后,vs2022创建的cmake项目竟然不能find_package到libssh 问题出在CMakeLists.txt,注意位置1、2、3的顺序一定不能变cmake_minimum_required (VERSION 3.20)#vcpkg————位置1 …

昆工昆明理工大学冶金最新复试真题及答案

--冶金工程考研809冶金物理化学有色冶金学有色金属冶金冶金过程及设备F002钢铁冶金学冶金调剂

《Transformer自然语言处理实战 : 使用Hugging Face Transformers库构建NLP应用》PDF免费下载

《Transformer自然语言处理实战》聚焦 Hugging Face Transformers 库,系统讲解 Transformer 模型在 NLP 任务中的应用。涵盖文本分类、命名实体识别、机器翻译等核心技术,并提供实践案例,帮助读者快速掌握模型微调与部署。适合 NLP 初学者及希望深入理解 Transformer 的开发…