CSP初赛复习-32-完善程序-双关键字排序
计数排序
计数排序(Counting Sort)是一种线性时间复杂度的排序算法,适用于整数排序且待排序的元素范围相对较小的情况。它的基本思想是通过统计每个元素出现的次数,然后根据统计信息将元素放回原数组的正确位置,从而实现排序
例题
如下几个数字进行计数排序
5 6 8 3 2 4
声明数组a[10]={0},分别把上面数字作为a数字下标,值为当前数+1
赋值后数组如下
下标 0 1 2 3 4 5 6 7 8 9
数值 0 0 1 1 1 1 1 0 1 0
逐一输出不为0的数的下标,如果值大于1,输出多次
完善程序
(计数排序)计数排序是一个广泛使用的排序方法。下面的程序使用双关键字计数排序,将n对10000以内的整数,从小到大排序
例如有三对整数(3,4) 、(2,4)、 (3,3),那么排序之后应该是(2,4) 、(3,3)、 (3,4)
输入第一行为n,接下来n行,第i行有两个数a[i]和b[i],分别表示第i对整数的第一关键字和第二关键字
从小到大排序后输出
数据范围 1<n<10^7,1<a[i], b[i]< 10^4
提示:应先对第二关键字排序,再对第一关键字排序。数组 ord[] 存储第二关键字排序的结果,数组 res[] 存储双关键字排序的结果
试补全程序
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 10000000;
const int maxs = 10000;
int n;
unsigned a[maxn], b[maxn],res[maxn], ord[maxn];
unsigned cnt[maxs + 1];
int main() {
scanf("%d", &n);//读取n对数
for (int i = 0; i < n; ++i)//逐一读取n对数
scanf("%d%d", &a[i], &b[i]);
memset(cnt, 0, sizeof(cnt));//cnt数组初始化为0
for (int i = 0; i < n; ++i)
①; // 利用 cnt 数组统计数量
for (int i = 0; i < maxs; ++i)
cnt[i + 1] += cnt[i];//前缀和累加 数字越大cnt[i]值越大,数字越大排序越靠后,从小到大排序
for (int i = 0; i < n; ++i)
②; // 记录初步排序结果
memset(cnt, 0, sizeof(cnt));//cnt数组重新初始化为0
for (int i = 0; i < n; ++i)
③; // 利用 cnt 数组统计数量
for (int i = 0; i < maxs; ++i)
cnt[i + 1] += cnt[i];//前缀和累加 数字越大cnt[i]值越大,数字越大排序越靠后,从小到大排序
for (int i = n - 1; i >= 0; --i)
④ // 记录最终排序结果
for (int i = 0; i < n; i++)
printf("%d %d", ⑤);
return 0;
}
①处应填()
A ++cnt[i]
B ++cnt[b[i]]
C ++cnt[a[i] * maxs + b[i]]
D ++cnt[a[i]]
答案 B
类似计数排序 数字做下标,多个相同的数字,cnt数组元素值累加
例:
b数组[4 4 3]
对应cnt数组
0 1 2 3 4
0 0 0 1 2
②处应填()
A ord[--cnt[a[i]]] = i
B ord[--cnt[b[i]]] = a[i]
C ord[--cnt[a[i]]] = b[i]
D ord[--cnt[b[i]]] = i
答案 D
cnt数组对出现的数字个数做前缀和存储
例
cnt数组
下标 0 1 2 3 4
数值 0 0 0 1 2
进行前缀和后
下标 0 1 2 3 4
数值 0 0 0 1 3
再次使用计数排序存入数组ord
例
b数组[4 4 3]
--cnt[b[0]]=--cnt[4]=2 后cnt[4]=2
--cnt[b[1]]=--cnt[4]=1
--cnt[b[2]]=--cnt[3]=0
存入ord数组,按第2关键字排序后对应的b数值下标
ord[2]=0
ord[1]=1
ord[0]=2
下标 0 1 2 3 4
数值 2 1 0 0 0
③处应填()
A ++cnt[b[i]]
B ++cnt[a[i] * maxs + b[i]]
C ++cnt[a[i]]
D ++cnt[i]
答案 C
类似计数排序 数字做下标,多个相同的数字,cnt数组元素值累加
例:
a数组[3 2 3]
对应cnt数组
0 1 2 3 4
0 0 1 2 0
④处应填()
A res[--cnt[a[ord[i]]]] = ord[i]
B res[--cnt[b[ord[i]]]] = ord[i]
C res[--cnt[b[i]]] = ord[i]
D res[--cnt[a[i]]] = ord[i]
答案 A
cnt数组对出现的数字个数做前缀和存储
例
cnt数组
下标 0 1 2 3 4
数值 0 0 1 2 0
进行前缀和后
下标 0 1 2 3 4
数值 0 0 1 3 0
ord 数组
下标 0 1 2 3 4
数值 2 1 0 0 0
再次使用计数排序存入数组res
此处放入res需要倒序for (int i = n - 1; i >= 0; --i),
因为如果两个数相同,涉及顺序问题,比如(3,3),(3,4)谁排前面问题
如果倒序先取出(3,4)放后面,再取(3,3)放前面,排序正常
反之,会出现(3,4)在(3,3)的前面
例
a数组[3 2 3]
--cnt[a[ord[2]]]=--cnt[a[0]]=--cnt[3]=1 后cnt[3]=2
--cnt[a[ord[1]]]=--cnt[a[1]]=--cnt[2]=0 后cnt[2]=0
--cnt[a[ord[0]]]=--cnt[a[2]]=--cnt[3]=2 后cnt[3]=1
存入res数组,按第1关键字排序后对应的a数值下标
res[1]=ord[0]=2
res[0]=ord[1]=1
res[2]=ord[2]=0
下标 0 1 2 3 4
数值 1 2 0 0 0
所以
结果为
1 2 0
(2,4) (3,3) (3,4)
⑤处应填()
A a[i], b[i]
B a[res[i]], b[res[i]]
C a[ord[res[i]]],b[ord[res[i]]]
D a[res[ord[i]]],b[res[ord[i]]]
答案 B
res[i]对应双关键字排序后的原a,b数组的下标
直接通过
a[res[i]], b[res[i]]输出即可
作者:newcode 更多资源请关注纽扣编程微信公众号

从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习

浙公网安备 33010602011771号