CSP 2022 提高级第一轮 - CSP/S 2022初试题 程序阅读第二题解析

一、代码查看

1  #include <iostream>
2  
3  using namespace std;
4  
5  const int MAXN = 105;
6  
7  int n, m, k, val[MAXN];
8  int temp[MAXN], cnt[MAXN];
9  
10 void init() 
11 {
12     cin >> n >> k;
13     for (int i = 0; i < n; i++) cin >> val[i];
14     int maximum = val[0];
15     for (int i = 1; i < n; i++)
16         if (val[i] > maximum) maximum = val[i];
17     m = 1;
18     while (maximum >= k) {
19         maximum /= k;
20         m++;
21     }
22 }
23 
24 void solve() 
25 {
26     int base = 1;
27     for (int i = 0; i < m; i++) {
28         for (int j = 0; j < k; j++) cnt[j] = 0;
29         for (int j = 0; j < n; j++) cnt[val[j] / base % k]++;
30         for (int j = 1; j < k; j++) cnt[j] += cnt[j - 1];
31         for (int j = n - 1; j >= 0; j--) {
32             temp[cnt[val[j] / base % k] - 1] = val[j];
33             cnt[val[j] / base % k]--;
34         }
35         for (int j = 0; j < n; j++) val[j] = temp[j];
36         base *= k;
37     }
38 }
39 
40 int main() 
41 {
42     init();
43     solve();
44     for (int i = 0; i < n; i++) cout << val[i] << ;
45     cout << endl;
46     return 0;
47 }

二、代码分析

这一题其实是标准的基数排序,不了解基数排序的可以参考我的博客。不过这里面的代码不是标准的写法,具体一下面解释为准。基数排序的思想及代码-CSDN博客

我们先看到 Init() 函数中。

void init() 
{
    cin >> n >> k;
    for (int i = 0; i < n; i++) cin >> val[i];
    int maximum = val[0];
    for (int i = 1; i < n; i++)
        if (val[i] > maximum) maximum = val[i];
    m = 1;
    while (maximum >= k) {
        maximum /= k;
        m++;
    }
}

这段代码前部分很显然是输入数据,并在其中找到最大值。既然是基数排序,那必须算出最大数的位数,但是一般都是除以十,而这里是除以k,所以k是进制,后面while循环里是在把它在k进制中的位数算出来并赋值给m。

void solve() 
{
    int base = 1;
    for (int i = 0; i < m; i++) { // 从低位到高位进行排序
        for (int j = 0; j < k; j++) cnt[j] = 0; // 初始化,因为k进制的上线是k - 1
        for (int j = 0; j < n; j++) cnt[val[j] / base % k]++;
        for (int j = 1; j < k; j++) cnt[j] += cnt[j - 1];
        for (int j = n - 1; j >= 0; j--) {
            temp[cnt[val[j] / base % k] - 1] = val[j];
            cnt[val[j] / base % k]--;
        }
        for (int j = 0; j < n; j++) val[j] = temp[j];
        base *= k;
    }
}

这段代码是排序的过程,请一定要看完下面一段!

首先,base是当前位数的单位,比如在二进制中,他便是1, 2, 4, 8, 16等,

在十进制中便是1, 10, 100, 1000等。

然后我们看循环,外循环有m次,表示从低位到高位进行排序。

第一个内循环:j 从 0 ~ k,表示对k进制所需要用到的下标进行初始化。比如说10进制中,我们只需要用0 ~ 9,因为个位、十位、百位、千位等等都一定在0 ~ 9范围之内。

第二个内循环:j 从 0 到 n,循环遍历 n 个数,记录0, 1, 2, 3....., k - 1在val数组中每个数的第base位出现的次数。比如说val = [11, 22, 31, 12, 21, 32],k = 10, 那base = 1时,cnt[1] = 3, cnt[2] = 3

第三个内循环:是对cnt进行前缀和处理,这也是对第base位排序时最终每个数的放置位置。举个例子,比如说val = [21, 31, 25, 65, 34, 76],k = 10, base = 1,那么执行这行之前,cnt = [0, 2, 0, 1, 2, 1......]

                  0  1  2  3  4  5

执行完这行后,cnt = [0, 2, 2, 3, 5, 6]

                                    0  1  2  3  4  5,

第四个内循环:同上个例子,执行第4个内循环,j 从 最后一个开始,到第一个结束。

第一次val[j] = 76,temp(这次排序的结果)[cnt[val[j] / base % k] - 1]

也就是temp[cnt[76 / 1 % 10] - 1]

           temp[cnt[6] - 1]

           temp[5 - 1]

           temp[4] = 76

然后cnt[ ]--

cnt[ ]为什么要减➖➖?

我们看到65这里,第一次就放在了temp[5 - 1]这里,如果不➖1,下一次25来了还是放在这里,这可不行,而且现在循环是倒着来的,原本在前面的必须晚点放进去才在前面。

三、题目分析

假设输入的 n 为不大于 100 的正整数,k 为不小于 2 且不大于 100 的正整数,val[i]在 int 表示范围内,完成下面的判断题和单选题:

判断题

1.  这是一个不稳定的排序算法。(错)

【基数排序是稳定的】

2.  该算法的空间复杂度仅与 n 有关。(错)

【cnt数组与k有关】

3.  该算法的时间复杂度为O(m(n+k))。(对)

【基数排序的时间复杂度是O(m(n+k)),m代表最大数的位数】

单选题

4.  当输入为“5 3 98 26 91 37 46”时,程序第一次执行到第 36 行,val[]数组的内容依次为(D)。

A. 91 26 46 37 98

B. 91 46 37 26 98

C.  98 26 46 91 37
D.  91 37 46 98 26

【第一次按“个”位排序,不过注意这里指的是三进制,转化完后分别为:

10122, 222, 10101, 1101, 1201,第一轮结束后为:

10101, 1101, 1201, 10122, 222,也就是十进制的

91, 37, 46, 98, 26,故选D】

5.  若 val[i]的最大值为 100,k 取(D)时算法运算次数最少。

A. 2

B.  3
C.  10
D.  不确定

【与n有关】

6.  当输入的 k 比 val[i]的最大值还大时,该算法退化为(C)算法。

A. 选择排序

B. 冒泡排序
C. 计数排序
D. 桶排序

【选择排序需要找最值并于其最终位置的元素交换;

冒泡排序需要双重循环,不停地将最值元素交换到首位;

桶排序需要对元素分类;

排除后只剩下C】

  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值