当前位置: 首页 > news >正文

如何理解计数排序和基数排序?

如何理解计数排序和基数排序?

先说二者的联系,计数排序是基数排序的依托,基数排序是计数排序的改良!

0x01.何谓计数排序?
从实现功能上看,它是将一串数据按一定顺序排出,每个数字出现多少次,就排多少遍,如下图:
image

绘图工具:draw.io

什么时候用?
当数据重复出现次数较多,且数据极差较小时采用。

0x02.如何实现计数排序?
1.实现查找最大值的函数,这里就是编程中常用的先定后变法(我起的名字):
就是说要找最值(最大值,最小值),先让第一个数组当最大值/最小值,后序遍历如果有比他更大/小的,就令新的元素来充当这个最值。

点击查看代码
int toFindMax(int arr[],int n){int max = arr[0];for(int i = 0;i < n;++i){if(arr[i] > max){max = arr[i];}}return max;
}

2.核心功能的实现:先统计每个数字出现的次数,再直接回填。
可能有读者不认识memset,简单来说就是给某一连续的内存地址统一赋值的,数组内存是连续的所以可以用。语法是:
memset(待赋值连续内存的首地址,赋值数据,缓冲区大小(该连续内存有多长))。
但是注意它是stdlib.h头文件里的函数。

点击查看代码
void countingSort(int arr[],int n){//取最大值int max = toFindMax(arr,n);//计数int count[40];memset(count,0,sizeof(int)* n);for(int i = 0;i < n;++i){count[arr[i]]++;}//把数据回填到arr里int index = 0;		//记录arr回填的最新位置for(int num = 0;num <= max;++num){   //从小到大找arr中存放的值while(count[num] > 0){			//每个值都回填应有的次数arr[index++] = num;count[num]--;}}
}

0x03.何谓基数排序
对于数据极差很大的一套数据,计数排序会很浪费内存,因为他会把从最小值到最大值都记录一遍,太浪费空间!
如果我们把数字按一位一位的排呢?每次只需要排0-9 9个数字,采用递归形式把arr中存放的每个数字都用计数排序来遍历,
不就很好地规避直接计数排序带来的空间开销大的风险吗?这就是基数排序的思想。

0x04.怎么实现基数排序?
如图:
image
看起来很麻烦,但其实很简单:首先每一位的处理操作都是相同的,所以我们采用递归的思想来看待此实现方法,我们可以写一个递归函数,
传入arr[],缓冲区大小n,再传入一个参数exp表示位数,实现在该位下的计数排序。

问:如何用一个参数表示取哪一位?
答:以123举例,如果我们要取个位3,123/100=123,123%10=3。取十位:123/101=12,12%10=2.取百位:123/10^3=1,1/10=1。
它的原理是我们要取哪一位,就让它的后面所有位数全部消失,它成为了新数字中的个位,然后直接/10求新数的个位即可。
这里唯一变换的是10的次方数,我们直接让它等于exp即可。

点击查看代码
void countSortByDigit(int arr[],int n,int exp){int temp[40];//临时储存int count[10] = {0};//存储0-9//统计0-9出现次数for(int i = 0;i < n;++i){count[(arr[i]/exp) % 10]++; //exp:10的次方数,这里(arr[i]/exp) % 10就是某一位上存储的数字}//使用前缀和思想保证排序稳定for(int i = 1;i < 10;++i){count[i] += count[i-1];  //注:从此count表示的不是0-9的一个数字出现了多少次,而是应存在的位置}//采用倒序插入法将arr的元素填入临时数组temp中//解释:由于为了排序稳定性,我们要确保指定位数上数字相同的元素相对位置不变,//count由大到小说明temp是倒着插入的,所以arr[i]要从高地址开始存,保证同时倒插。for(int i = n-1;i >= 0;--i){int digit = (arr[i] / exp) % 10; //取arr中第i个元素指定位的数字temp[count[digit] - 1] = arr[i];//存入临时数组中--count[digit];}//回填(在原数组上排序)for(int i = 0;i < n;++i){arr[i] = temp[i];}
}

上述代码用到了前缀和思想。那么,什么是前缀和呢?

前缀和就是:从第一个数开始,一路加到当前位置,得到的 “累计值”。

在该思想的指导下,count实现了从服务于一个数字的计数到服务于确定该数字最终位置的转变。
以只有个位的一组数字为例,看看count是怎样实现功能的:

image
有人说,不对呀,为什么不存在的元素也有count值?
int digit = (arr[i] / exp) % 10; //取arr中第i个元素指定位的数字
temp[count[digit] - 1] = arr[i];//存入临时数组中
不妨多品味一下这两行代码,digit从arr中取,根据digit对应的count值确定该数字应存的位置然后插进去。不存在的数在第一步就pass掉了。
这里第二个2用红底标注用于判断排序的稳定性,显然稳定(两个2相对位置不变)

最后总函数使用遍历将每一位都这样处理,就大功告成!

点击查看代码
//需要调用上面的toFindMax()
void radixSort(int arr[],int n){int max = toFindMax(arr,n);for(int exp = 1;max /exp > 0;exp *= 10){	//从个位到最大位,注意exp从1开始,说明我们先排个位countSortByDigit(arr,n,exp);}
}
为什么先排各位?从最高位开始不行嘛? 比如31,13,先排最高位则是13,31,再排最低位则是31,13 。❌️ 再比如31,21,13,先排最低位31,21,23,再排最高位21,23,31 ✅️ 显然位越高影响越大,先排最高位容易导致效果被最低位覆盖掉。 所以先排最低位

0x05总结复盘

计数排序从整体出发,直接计数并排序,当数据极值很大时浪费空间;
基数排序则从每一位出发,在每一位进行计数排序,最终完成排序。

学习到的关键思想:前缀和思想

http://www.jsqmd.com/news/507316/

相关文章:

  • 闲置瑞祥商联卡别浪费,这样处理更省心 - 抖抖收
  • Stremio-web测试覆盖率提升:从60%到90%的实战技巧
  • 2026 年 GEO 优化公司 TOP5:为企业增长提供核心技术支撑 - 速递信息
  • 在线教育系统安全设计实战:如何用威胁建模避免SQL注入和数据泄露
  • 品当下清欢,享此刻宁静hhh
  • 2026企业级会议系统怎么挑?保伦股份全链路方案实测
  • 智能充气泵方案pcba方案设计研发
  • GEO 优化服务商避坑指南:2026 年选型必看核心标准 - 速递信息
  • 计算机视觉领域核心会议期刊全称与缩写速查指南
  • 2026探讨埃格AIGE IBB创新能力怎么样靠谱吗 - 工业品牌热点
  • Mathtype公式编辑加速:LiuJuan20260223Zimage集成方案
  • 规范事故处置:2026道路交通事故快速勘查系统公司推荐 - 品牌2026
  • 高等数学零点定理实战:3个例题教你搞定考研压轴题
  • Stremio-web视频多音轨支持:语言切换与音频控制全指南
  • 2026年河北口碑好的机械制造公司推荐,细聊天丰机械厂合作客户、口碑及设备情况 - myqiye
  • wzry项目移动端适配方案:从响应式到PWA的完整实践
  • 如何在RTX 4090上使用kohya_ss的BF16混合精度训练:完整指南与性能优化
  • 从理论到部署:知识图谱与大语言模型融合的工程化实战手册
  • 效率直接起飞 9个AI论文网站测评:开源免费+毕业论文写作全攻略
  • 2026年总结专业的电商代运营公司,潮州地区值得推荐的公司有哪些 - 工业推荐榜
  • 及时恢复畅通:2026一般程序事故道路交通事故快速勘查系统厂商推荐 - 品牌2026
  • Medusa订阅经济:构建高效周期性付费与会员制电商模式的完整指南
  • 探讨滤油机设备价格,如何选到高性价比产品 - 工业设备
  • Python3.10+Pyside2实战:手把手教你打造Modbus RTU通信界面(附源码)
  • 聊聊2026年全国值得选购的变压器滤油机,能降低油润滑性能的厂家有哪些 - 工业推荐榜
  • Python+OpenCV实战:高效处理16位遥感影像转8位的分块优化技巧
  • 破解新能源汽车铜铝排折弯痛点:四维全协同精准折弯方法论如何实现产能与良率双飞跃? - 速递信息
  • Crossplane性能基准测试:百万级资源调度能力评估
  • ECU-TEST新手必看:从下载到试用的完整避坑指南(附Python脚本调用技巧)
  • 解密pandas_ta策略引擎:如何用3行代码批量计算50+技术指标