Skip to content

2021年 408 数据结构 第 42 题

数据结构2021年综合题5分

题目

已知某排序算法如下:

c
void cmpCountSort(int a[], int b[], int n) {
    int i, j, *count;
    count = (int *)malloc(sizeof(int) * n);   // C++ 语言:count = new int[n];
    for (i = 0; i < n; i++) count[i] = 0;
    for (i = 0; i < n - 1; i++)
        for (j = i + 1; j < n; j++)
            if (a[i] < a[j]) count[j]++;
            else            count[i]++;
    for (i = 0; i < n; i++) b[count[i]] = a[i];
    free(count);   // C++ 语言:delete count;
}

请回答下列问题:

(1) 若有 int a[] = {25, -10, 25, 10, 11, 19}, b[6];,则调用 cmpCountSort(a, b, 6) 后数组 b 中的内容是什么?

(2) 若 a 中含有 n 个元素,则算法执行过程中,元素之间的比较次数是多少?

(3) 该算法是稳定的吗?若是,则阐述理由;否则,修改为稳定排序算法。

解析

算法本质:count[k] 是 a[k] 在数组中的最终排名(从 0 起)

编者注(生僻术语):这就是经典的「比较计数排序 / Comparison Counting Sort」。它跟桶式的 Counting Sort(值域计数)完全是两个东西,别混。本题的 count[] 数组不是值域桶,而是"每个原位置元素的最终排名"。

外层 i + 内层 j(i < j)枚举所有"位置对":

  • a[i] < a[j] → count[j]++(j 处的元素被发现"大于"了一个,排名 +1);
  • 否则(包括相等)→ count[i]++(i 处的元素排名 +1)。

每对位置贡献恰好 1 次累加,n 个元素共 对。最后 count[i] = a[i] 的最终下标,所以 b[count[i]] = a[i] 一遍写就排好了。

(1) 给定数组的 b 内容

答案:b = [-10, 10, 11, 19, 25, 25]

逐对模拟(n = 6)。下表记录每一轮内层循环结束后 count[] 的累加:

i内层循环 (j 与判断)对 count 的累加累计 count
0 (a=25)j=1..5 共 5 对,全是 25 ≥ a[j],均 count[0]++count[0]+=5[5,0,0,0,0,0]
1 (a=−10)j=2..5,−10 < a[j] 全成立,分别 count[j]++count[2,3,4,5] 各+1[5,0,1,1,1,1]
2 (a=25)j=3..5,25 ≥ a[j] 全成立,均 count[2]++count[2]+=3[5,0,4,1,1,1]
3 (a=10)j=4 (10<11) → count[4]++;j=5 (10<19) → count[5]++count[4,5] 各+1[5,0,4,1,2,2]
4 (a=11)j=5 (11<19) → count[5]++count[5]+=1[5,0,4,1,2,3]
5(内层 0 对)[5,0,4,1,2,3]

最终 count = [5, 0, 4, 1, 2, 3]。按 b[count[i]] = a[i] 散列回去:

ia[i]count[i]写入
0255b[5] = 25
1−100b[0] = −10
2254b[4] = 25
3101b[1] = 10
4112b[2] = 11
5193b[3] = 19

b = [−10, 10, 11, 19, 25, 25]

(2) 元素之间的比较次数

答案: 次(n = 6 时为 15 次)。

外层 i 取 0..n−2,内层 j 取 i+1..n−1:

每对 (i, j) 恰好比较 1 次(if (a[i] < a[j]) 这一行)。比较次数与输入数据无关,永远是

(3) 稳定性 + 改造为稳定

答案:原算法不稳定。修改:把 if (a[i] < a[j]) 改为 if (a[i] <= a[j])

为什么原算法不稳定:本例中两个 25(原位置 0 与 2)排序后落到位置 5 和 4——原本靠前的 25 跑到了靠后,相对顺序颠倒。

根因在于代码遇到相等时走 else 分支:当 i < j 且 a[i] == a[j] 时,count[i]++靠前的位置反而排名更大,结果靠前的元素被推到后面。

修改方法:让"相等"也走 count[j]++——即靠后的位置排名 +1,靠前的保留小排名:

c
for (i = 0; i < n - 1; i++)
    for (j = i + 1; j < n; j++)
        if (a[i] <= a[j]) count[j]++;     // 改 < 为 <=
        else              count[i]++;

验证修改后稳定:再跑一次本例,i=0、j=2 时 a[0]=25, a[2]=25,25 ≤ 25 成立 → count[2]++,count[0] 不变。最终位置 0 的 25 排名仍为 4(少 1),位置 2 的 25 排名为 5——原顺序保持,稳定。

编者注(生僻术语):「稳定排序」指对所有相等关键字,排序后的相对顺序与排序前一致。判断方法是构造一个有重复关键字的输入,看输出里相等元素的相对位置是否颠倒——本题第 (1) 问的 a 中两个 25 就是天然的稳定性测试用例,可见命题人本意就是引出 (3)。

最后更新:

⚠️ 这道题暂未配可视化,欢迎在 CodeBrick 反馈区告诉我们你想看哪道题