小班课文档

2026-05-11 练习题答案与解析

排序、查找和散列 答案与解析

单项选择题

1. 给定一个 N 个相异元素构成的有序数列,设计一个递归算法实现数列的二分查找,请问递归调用栈的最小容量应为( )。

答案:D. ⌊log₂(N+1)⌋

解析: 二分查找每次将搜索区间减半,递归深度等于查找路径长度。对于 N 个元素的有序数列,最坏情况下递归深度为 ⌊log₂(N+1)⌋。例如 N=1 → 1 层,N=3 → 2 层,N=7 → 3 层,与公式吻合。每层递归占用一个栈帧,所以栈的最小容量即为该递归深度。


2. 1GB 数据排序、可用内存仅 100MB,最适合的排序算法是( )。

答案:B. 归并排序

解析: 数据规模远超可用内存,必须采用外部排序。归并排序天然支持分块处理:先将大数据切成若干个能装入内存的块、对每块独立排序后写回磁盘,再多路归并合并所有块。归并阶段只需要在内存中维护每路的少量缓冲区即可。

堆排序和快速排序都假设数据全部驻留内存,无法直接处理超内存的数据;插入排序更不适合大规模数据。


3. 下列哪种是不稳定排序?

答案:D. 希尔排序

解析: 稳定排序指相等关键字在排序后保持原有相对顺序。

  • 冒泡排序:只在严格大于时交换,相等元素不动 → 稳定
  • 归并排序:合并时遇到相等元素优先取左侧 → 稳定
  • 直接插入排序:从右向左找插入位置,相等时不越过 → 稳定
  • 希尔排序:按增量分组进行插入排序,相等元素可能被分到不同子序列中改变相对顺序 → 不稳定

4. 哪一组排序算法最坏情况下时间复杂度大 O 表示相同?

答案:C. 快速排序和选择排序

解析: 各算法的最坏情况时间复杂度:

算法最坏情况
快速排序O(n²)(基准每次都极端不平衡)
堆排序O(n log n)
归并排序O(n log n)
直接插入排序O(n²)
选择排序O(n²)
冒泡排序O(n²)

只有 C 选项中两者都是 O(n²)。


5. 多关键字排序,先按 key1 非递减、key1 相同时再按 key2 非递减,应选哪种方法?

答案:D. 先按 key2 值进行直接选择排序,再按 key1 值进行冒泡排序

解析: 多关键字排序的标准做法是:先排次关键字,再排主关键字,且最后一次排序必须使用稳定算法——这样主关键字相同时才会保持次关键字的相对顺序。

  • 冒泡排序:稳定
  • 直接选择排序:不稳定

因此最后一次必须用冒泡,主关键字 key1 由冒泡排序——选 D。

A、C 顺序错误;B 最后用了不稳定的选择排序,会破坏 key2 的有序性。


6. 有 n² 个整数,找到其中最小整数需要比较次数至少为( )次。

答案:C. n²-1

解析: 在 m 个互异元素中找最小值,每次比较最多淘汰一个候选元素。要确定最小值需要淘汰其余 m−1 个元素,所以至少需要 m−1 次比较。这里 m = n²,故为 n²−1 次。


7. 散列检索 38 需要进行几次比较?

答案:B. 2

0123456789101112
263817334825

解析: 此处"再散列"为双重散列(double hashing),即用 H₂(key) 作为探查步长。

  • h(38) = 38 mod 13 = 12,位置 12 是 25(h(25)=12,先占位)≠ 38 → 第 1 次比较失败
  • 计算步长 H₂(38) = 38 mod 3 = 2
  • 下一探查位置 = (12 + 2) mod 13 = 1,位置 1 = 38 → 第 2 次比较成功

2 次关键码比较。


判断题

8. 希尔排序每趟都要调用直接插入排序,所以效率比直接插入排序差。

答案:B. 错误

解析: 希尔排序虽然每趟都基于直接插入排序,但通过递减增量分组,让远距离元素跨步交换,使整体逐渐"基本有序"。这样后续插入排序遇到的子序列长度小、移动距离短,整体效率显著提高。

希尔排序平均时间复杂度约 O(n^1.3)(取决于增量序列),优于直接插入排序的 O(n²)。


9. 直接插入排序、冒泡排序、希尔排序都是在数据正序时比逆序快。

答案:A. 正确

解析:

  • 直接插入排序:正序时每个元素只需比较 1 次 → O(n);逆序时需移动所有前驱 → O(n²)
  • 冒泡排序:正序时一趟扫描即可结束 → O(n);逆序时需 n−1 趟 → O(n²)
  • 希尔排序:正序时各子序列已经有序,每次插入排序的比较和移动都极少;逆序时需较多比较和移动

三者满足"正序快于逆序"。


10. 散列表关键码比较次数仅取决于散列函数和冲突处理方法。

答案:B. 错误

解析: 关键码比较次数还与装填因子(λ\lambda = n/m) 密切相关。装填因子越大,冲突概率越高,

所以"仅取决于两个因素"的说法是不全面的。


11. 含 N 个结点构建二叉最小堆,时间复杂度为 O(N log₂N)。

答案:B. 错误

解析: 建堆的正确时间复杂度是 O(N)

虽然单次 siftDown 调整是 O(log N),但建堆是从最后一个非叶节点开始向上做 siftDown,底层节点多但下沉路径短,顶层节点少但下沉路径长。数学上:

h=0logNN2h+1h=O(N)\sum_{h=0}^{\log N} \frac{N}{2^{h+1}} \cdot h = O(N)

自底向上 siftDown 建堆是 O(N)。


填空题

12. 二分检索应采用什么存储形式?

答案:顺序存储

解析: 二分检索每一步都需要 O(1) 时间访问"中间位置"的元素,只有顺序存储(数组)支持下标随机访问。链式存储要从头遍历到中点,需要 O(n) 时间,会让二分检索退化为 O(n log n),失去意义。


13. 在 1000 个元素中找最小的前 5 个,最快的算法是?

答案:堆排序

解析: 这是经典的 top-k 问题。

算法找前 5 小所需操作
冒泡排序5 趟扫描,每趟 ~n 比较,约 5×1000 = 5000
快速排序必须排完整个序列 → O(n log n) ≈ 10000
归并排序必须完整归并 → O(n log n) ≈ 10000
堆排序建小根堆 O(n) ≈ 1000,再 5 次 delMin(每次 O(log n)≈10),共 ≈ 1050

堆排序最快,是 top-k 类问题的天然选择(实际生产中通常使用 size=k 的最大堆,复杂度为 O(n log k))。


14. n 个数据对象的二路归并排序中,每趟归并的时间复杂度为?

答案:O(n)

解析: 每趟归并将相邻有序子序列两两合并,每个元素只被读取/比较常数次,所以单趟归并时间为 O(n)。由于归并排序共需 ⌈log₂n⌉ 趟,所以归并排序总复杂度 O(n log n)。


15. 希尔排序与归并排序第一轮结果(降序)

输入:[46, 70, 56, 38, 40, 80, 60, 22]

(1) 希尔排序 gap=4 第一趟答案: [46, 80, 60, 38, 40, 70, 56, 22]

解析: 按下标差 4 分组,得到 4 个子序列,各自降序排列:

下标子序列降序后
0, 4(46, 40)(46, 40)
1, 5(70, 80)(80, 70)
2, 6(56, 60)(60, 56)
3, 7(38, 22)(38, 22)

按下标回填即得结果。

(2) 归并排序第一轮答案: [70, 46, 56, 38, 80, 40, 60, 22]

解析: 把每个元素视为长度为 1 的子序列,相邻两两归并(降序,大者在前):

  • (46, 70) → 70, 46
  • (56, 38) → 56, 38
  • (40, 80) → 80, 40
  • (60, 22) → 60, 22

16. 快速排序第一次划分(pivot = 46,非递减)

答案:[40, 38, 46, 56, 70, 80]

解析: 取首元素 46 为基准,使用双指针法(left/right 交替扫描):

操作数组leftright
初始(保存 pivot=46)[_, 70, 56, 38, 40, 80]05
右→左找 < 46:40,放入 0[40, 70, 56, 38, _, 80]04
左→右找 > 46:70,放入 4[40, _, 56, 38, 70, 80]14
右→左找 < 46:38,放入 1[40, 38, 56, _, 70, 80]13
左→右找 > 46:56,放入 3[40, 38, _, 56, 70, 80]23
相遇于 2,放回 pivot[40, 38, 46, 56, 70, 80]

划分完成,pivot 左侧元素 ≤ 46,右侧元素 ≥ 46。


17. 拉链法散列地址 1 的链中有几个记录?

答案:4

解析: 计算每个 key mod 13:

key19142316820842755111079
地址611013761311101

落在地址 1 的关键字:14, 1, 27, 79,共 4 个


18. 删除最小堆 [8, 15, 10, 21, 34, 16, 12] 的堆顶后比较次数

答案:3

解析:

  1. 用末尾元素 12 替换堆顶 → [12, 15, 10, 21, 34, 16]
  2. 第 1 层 siftDown(12 在 index 0):
    • 比较两个孩子 15 和 10,取较小者 10 → 第 1 次比较
    • 比较 12 和 10,12 > 10 → 交换 → 第 2 次比较
    • 结果:[10, 15, 12, 21, 34, 16]
  3. 第 2 层 siftDown(12 在 index 2,只有一个孩子 16):
    • 比较 12 和 16,12 < 16 → 停止 → 第 3 次比较

3 次关键字比较。


综合题

19. 奇偶交换排序

(a) 序列 [18, 73, 5, 10, 68, 99, 27, 10] 前 4 趟排序结果

类型比较位置结果
1奇 (i=1,3,5,7)(18,73)(5,10)(68,99)(27,10)[18, 73, 5, 10, 68, 99, 10, 27]
2偶 (i=2,4,6)(73,5)(10,68)(99,10)[18, 5, 73, 10, 68, 10, 99, 27]
3奇 (i=1,3,5,7)(18,5)(73,10)(68,10)(99,27)[5, 18, 10, 73, 10, 68, 27, 99]
4偶 (i=2,4,6)(18,10)(73,10)(68,27)[5, 10, 18, 10, 73, 27, 68, 99]

加粗的是发生交换的对。

(b) 是否稳定?

答案:稳定

解析: 算法只在 a[i] > a[i+1]严格大于时才交换相邻元素,相等元素的相对顺序不会改变,所以是稳定排序。这也是基于相邻交换类排序通常稳定的共同原因(同冒泡排序)。

(c) 正序 / 逆序时的比较次数与交换次数

正序情况(如 [1, 2, ..., n]):

  • 一趟奇扫描:i=1,3,…,比较 ⌊n/2⌋ 次,全部 aᵢ ≤ aᵢ₊₁,无交换 → change1 = False
  • 一趟偶扫描:i=2,4,…,比较 ⌊(n−1)/2⌋ 次,无交换 → change2 = False
  • 进入下一轮 while 时 change1 和 change2 都为 False,退出循环
数值
比较次数⌊n/2⌋ + ⌊(n−1)/2⌋ = n − 1
交换次数0

逆序情况(如 [n, n−1, ..., 1]):

每对相邻元素都需交换,每次交换消除一个逆序对。逆序数组的总逆序对数恰为 C(n,2)。需要约 ⌈n/2⌉ 轮(odd+even)才能完全有序,再加一轮检测无变化才能跳出 while。每轮做 (n−1) 次比较。

数值
比较次数(⌈n/2⌉ + 1)(n − 1) ≈ n²/2,量级 O(n²)
交换次数n(n − 1) / 2

小例(n=4 逆序 [4,3,2,1])验证:

奇扫描后偶扫描后该轮交换
1[3,4,1,2][3,1,4,2]3
2[1,3,2,4][1,2,3,4]3
3无变化无变化0
  • 总比较 = 3 × 3 = 9 次 ≈ n²/2
  • 总交换 = 6 = n(n−1)/2 ✓

结论: 正序为最优情况(线性 O(n)),逆序为最坏情况(平方 O(n²)),与冒泡排序的特性一致。

On this page

单项选择题1. 给定一个 N 个相异元素构成的有序数列,设计一个递归算法实现数列的二分查找,请问递归调用栈的最小容量应为( )。2. 1GB 数据排序、可用内存仅 100MB,最适合的排序算法是( )。3. 下列哪种是不稳定排序?4. 哪一组排序算法最坏情况下时间复杂度大 O 表示相同?5. 多关键字排序,先按 key1 非递减、key1 相同时再按 key2 非递减,应选哪种方法?6. 有 n² 个整数,找到其中最小整数需要比较次数至少为( )次。7. 散列检索 38 需要进行几次比较?判断题8. 希尔排序每趟都要调用直接插入排序,所以效率比直接插入排序差。9. 直接插入排序、冒泡排序、希尔排序都是在数据正序时比逆序快。10. 散列表关键码比较次数仅取决于散列函数和冲突处理方法。11. 含 N 个结点构建二叉最小堆,时间复杂度为 O(N log₂N)。填空题12. 二分检索应采用什么存储形式?13. 在 1000 个元素中找最小的前 5 个,最快的算法是?14. n 个数据对象的二路归并排序中,每趟归并的时间复杂度为?15. 希尔排序与归并排序第一轮结果(降序)16. 快速排序第一次划分(pivot = 46,非递减)17. 拉链法散列地址 1 的链中有几个记录?18. 删除最小堆 [8, 15, 10, 21, 34, 16, 12] 的堆顶后比较次数综合题19. 奇偶交换排序(a) 序列 [18, 73, 5, 10, 68, 99, 27, 10] 前 4 趟排序结果(b) 是否稳定?(c) 正序 / 逆序时的比较次数与交换次数