一、代码查看
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】