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]]输出即可
posted @ 2023-09-13 22:59  new-code  阅读(128)  评论(0)    收藏  举报