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

洛谷 P1177:【模板】排序 ← 基数排序实现

【题目来源】
https://www.luogu.com.cn/problem/P1177

【题目描述】
将读入的 N 个数从小到大排序后输出。

【输入格式】
第一行为一个正整数 N。
第二行包含 N 个空格隔开的正整数 ai,为你需要进行排序的数。

【输出格式】
将给定的 N 个数从小到大输出,数之间空格隔开,行末换行且无空格。

【输入样例】
5
4 2 4 5 1

【输出样例】
1 2 4 4 5

【说明/提示】
对于 20% 的数据,有 1≤N≤10^3;
对于 100% 的数据,有 1≤N≤10^5,
1≤ai≤10^9

【算法分析】
● 基数排序(Radix Sort)是一种‌非比较型整数排序算法‌,通过逐位分配和收集元素实现排序‌。
基数排序仅适用于整数或可转换为数字的字符串‌的排序。其核心原理是将整数按位数切割,从最低位(LSD)或最高位(MSD)开始依次排序,利用桶排序或计数排序作为子过程‌。基数排序是倍增法实现后缀数组(Suffix Array)的核心排序工具,其高效的 O(n) 时间复杂度直接决定了后缀数组整体算法的时间复杂度‌。

● 基数排序的步骤
(一)预处理阶段‌:计算数组中元素的最大位数,确定基数排序的轮数。

int maxbit(int n,int a[]) {int imax=0;for(int i=1; i<=n; i++) {int cnt=0, t=a[i];while(t!=0) {t=t/10;cnt++;}imax=max(imax,cnt);}return imax;
}

(二)按位排序阶段‌(从最低位到最高位)
(1)统计频次‌:遍历数组 a[],统计当前轮按位排序时当前位上每个数字(0-9)的出现次数,存储于频次数组 freq[] 中。

for(int i=0; i<=9; i++) freq[i]=0;
for(int i=1; i<=n; i++) {int x=(a[i]/div)%10;freq[x]++;
}

(2)计算前缀和:对频次数组 freq[] 做前缀和 freq[i]+=freq[i-1],i∈[1,9]。其中,freq[0] 的值为上步所得。此时,若有 freq[i]=x,则表示在按位排序的当前轮执行后,数组 a[] 中的各元素在当前位小于等于 i 的数有 x 个在基数排序中,计算前缀和的主要目的是‌为了快速确定数组 a[] 的元素在按位排序的当前轮执行后,它们在所得的有序序列中的位次

for(int i=1; i<=9; i++) {freq[i]+=freq[i-1];
}

(3)分配位次:逆序遍历数组 a[],依据元素 a[i] 的当前轮当前位元素的频次,将元素 a[i] 放入临时数组 rk[] 的对应位置。逆序遍历数组 a[],是为了‌保持基数排序的稳定性‌。为了应对数组 a[] 中,多个元素在按位排序的当前轮的当前位均为相同的 t,则需在给一个 t 安置后,执行 freq[t]--,为下一个相同的 t 预留正确的位次。

for(int i=n; i>=1; i--) {int t=(a[i]/div)%10;rk[freq[t]]=a[i];freq[t]--;
}

(4)更新数组‌:将当前轮按位排序结果 rk[] 覆盖原数组 a[],为下轮排序做准备。

for(int i=1; i<=n; i++) {a[i]=rk[i];
}


【基数排序算法实例】
假设我们有一个待排序的数组:a=[712, 303, 4, 18, 89, 999, 70, 26]。
在预处理阶段‌,通过计算可得数组 a[] 中元素的最大位数为 3,也即基数排序的轮数。
下面各步骤,以基数排序的第一轮,也即以数组 a[] 中各元素按位排序时的个位为例,进行算法陈述。

步骤1:统计频次‌
首先创建一个频次数组 freq(长度为 10,对应数字 0~9),统计数字 0~9 出现的次数。显然,对于给定的数组
a=[712, 303, 4, 18, 89, 999, 70, 26],其频率数组 freq=[1, 0, 1, 1, 1, 0, 1, 0, 1, 2]。这个频率数组在基数排序的下一步中会转换为前缀和数组,快速确定数组 a[] 的元素在按位排序的当前轮执行后,它们在所得的有序序列中的位次,目的是实现稳定排序。

步骤2:计算前缀和‌
执行
for(int i=1; i<=9; i++) freq[i]+=freq[i-1]; 后,频次数组 freq 的值变为 [1, 1, 2, 3, 4, 4, 5, 5, 6, 8]。此时,频次数组 freq 的语义更新为前缀和数组 freq,其含义如下所示。
freq[0] = 1:表示所有小于等于 0 的数字有 1 个
freq[1] = 1:表示所有小于等于 1 的数字有 1 个
freq[2] = 2:表示所有小于等于 2 的数字有 2 个
freq[3] = 3:表示所有小于等于 3 的数字有 3 个
……
freq[8] = 6:表示所有小于等于 8 的数字有 6 个
freq[9] = 8:表示所有小于等于 9 的数字有 8 个

步骤3:分配位次

逆序遍历数组 a[],依据元素 a[i] 的当前轮当前位元素的频次,将元素 a[i] 放入临时数组 rk[] 的对应位置。
i=8:a[8]=26 → t=(26/1)%10=6 → rk[freq[6]]=rk[5]=26 → freq[6]减为4
i=7:a[7]=70 → t=(70/1)%10=0 → rk[freq[0]]=rk[1]=70 → freq[0]减为0
i=6:a[6]=999 → t=(999/1)%10=9 → rk[freq[9]]=rk[8]=999 → freq[9]减为7
i=5:a[5]=89 → t=(89/1)%10=9 → rk[freq[9]]=rk[7]=89 → freq[9]减为6
i=4:a[4]=18 → t=(18/1)%10=8 → rk[freq[8]]=rk[6]=18 → freq[8]减为5
i=3:a[3]=4 → t=(4/1)%10=4 → rk[freq[4]]=rk[4]=4 → freq[4]减为3
i=2:a[2]=303 → t=(303/1)%10=3 → rk[freq[3]]=rk[3]=303 → freq[3]减为2
i=1:a[1]=712 → t=(712/1)%10=2 → rk[freq[2]]=rk[2]=712 → freq[2]减为1

步骤4:更新数组‌
将当前轮按位排序结果 rk[]
覆盖原数组 a[],为下轮排序做准备。
此时 a[1]~a[8] 的值为 rk[1]~rk[8] 的值:70, 712, 303, 4, 26, 18, 89, 999。
显然,70, 712, 303, 4, 26, 18, 89, 999 是按个位从小到大排序后的结果,之后将以此数据为基础进行下一轮(十位)的按位排序。


【算法代码: 基数排序求解洛谷P1177的代码

#include <bits/stdc++.h>
using namespace std;const int maxn=1e5+5;
int a[maxn];
int rk[maxn],freq[maxn];int maxbit(int n,int a[]) {int imax=0;for(int i=1; i<=n; i++) {int cnt=0, t=a[i];while(t!=0) {t=t/10;cnt++;}imax=max(imax,cnt);}return imax;
}void Radix_Sort(int n,int a[]) {int cnt=maxbit(n,a);int div=1;while(cnt--) {for(int i=0; i<=9; i++) {freq[i]=0;}for(int i=1; i<=n; i++) {int t=(a[i]/div)%10;freq[t]++;}for(int i=1; i<=9; i++) {freq[i]+=freq[i-1];}for(int i=n; i>=1; i--) {int t=(a[i]/div)%10;rk[freq[t]]=a[i];freq[t]--;}for(int i=1; i<=n; i++) {a[i]=rk[i];}div=div*10;}
}int main() {int n;cin>>n;for(int i=1; i<=n; i++) {cin>>a[i];}Radix_Sort(n,a);for(int i=1; i<=n; i++) {cout<<a[i]<<" ";}return 0;
}/*
in:
5
12345 12 123 1 12out:
1 12 12 123 12345
*/




【参考文献】
https://www.cnblogs.com/pdpdzaa/p/17522307.html
https://www.cnblogs.com/pdpdzaa/p/17532682.html
https://zhuanlan.zhihu.com/p/655379377
https://www.cnblogs.com/000zwx000/p/12330710.html


 

http://www.dtcms.com/a/535960.html

相关文章:

  • 株洲网站设计外包首选中国可信网站查询
  • 物联网智慧医疗:告别胶片时代的就医革命
  • 3步实现MQTT远程连接!EMQX+cpolar构建物联网消息高速公路
  • 怎么注册微网站织梦建设网站全过程
  • [无人机sdk] `FlightController` | startTakeoffSync() | actionSync()
  • [linux仓库]线程与进程的较量:资源划分与内核实现的全景解析[线程·贰]
  • Flutter开发HarmonyOS鸿蒙App商业项目实战已出炉
  • 宁波网站建设制作公司排名网站优化外链怎么做
  • 开发做网站公司国内网站空间推荐
  • 罗克韦尔PLC通过Modbus TCP转EtherNet/IP智能网关与港口中央监控云平台的互通方案解析
  • 学习threejs,打造交互式泡泡、粒子特效与科幻氛围​​
  • Linux小课堂: Apache虚拟主机配置之基于IP与域名的服务器部署指南
  • MCU的时钟系统
  • OpenCV 4.1.2 SDK 静态库作用与功能详解
  • Hive数据仓库:架构原理与现代化实践指南
  • 当选择中药饮片时,如何确保性价比高的优质选择?
  • FFmpeg 基本数据结构 URLProtocol分析
  • Socket和Websocket编程的区别
  • 龙岗区住房和建设局官方网站巩义做网站优化
  • 网站建设的扁平化设计微信公众平台官网登录入口网页版
  • 《国家哲学社会科学文献中心:类似网站列表》
  • DC-DC降压芯片120V转12V5V3.3V4A大电流大功率H6253L 150V高耐压内置MOS管 电动车电源芯片
  • 学习和掌握RabbitMQ及其与springboot的整合实践(篇一)
  • 【数据集】【YOLO】目标检测游泳数据集 4481 张,溺水数据集,YOLO河道、海滩游泳识别算法实战训练教程。
  • 从“十五五”规划看中国视频基础设施的下一个五年:SmartMediaKit 的战略跃迁与时代机遇
  • ionic 列表:详解移动端UI设计中的列表组件
  • 做网站销售电话术语住房和城乡建设部网站评估
  • C#数据类型:List
  • 帝国网站地图模板酒类网站建设
  • [nanoGPT] 检查点 | `ckpt.pt`记忆 | 预训练模型加载`from_pretrained`