一 真题2010-9
2010-09. 已知一个长度为 16 的顺序表 LL ,其元素按关键字有序排列。若采用折半查找法查找一个 LL 中不存在的元素,则关键字的比较次数最多的是( )。
A. 4
B. 5
C. 6
D. 7
二 题目要素解析
核心概念 :折半查找(Binary Search)
- 适用条件:有序的顺序表(数组)。
- 基本思想:将待查关键字与表中间位置的元素比较,若相等则查找成功;若不等,则缩小查找范围(前半部分或后半部分),重复上述过程。
查找判定树:
- 折半查找的过程可以用一棵二叉排序树来描述,称为判定树。
- 树中每个节点代表表中的一个元素。
- 树的高度 h 决定了最多比较次数。
失败节点:
- 查找失败的情况对应判定树中的外部节点(空节点)。
- 查找失败时的比较次数 = 从根节点到对应外部节点路径上的内部节点个数。
三 哔哔详解
方法一:公式计算法(推荐)
对于长度为nnn 的有序表,折半查找的判定树高度 hhh 为:
h=⌊log2n⌋+1h= \lfloor \log_2 n \rfloor +1h=⌊log2n⌋+1
本题中 n=16n=16n=16:
- 计算 log216=4log_2 16=4log216=4。
- 所以判定树的高度 h=4+1=5h=4+1=5h=4+1=5。
- 这意味着,查找成功时最多比较 4 次(对应叶子节点),查找失败时最多比较 5 次(对应最深的外部节点)。
方法二:模拟查找过程(验证)
假设顺序表元素为 a_1, a_2, \\ldots, a_{16} 。
我们要查找一个比所有元素都大的数(例如 key \> a_{16} ),这是比较次数最多的情况。
- 第 1 次比较: low = 1 , high = 16 → mid = (1 + 16)/2 = 8 。 key \> a_8 ,查找右半部分。
- 第 2 次比较: low = 9 , high = 16 → mid = (9 + 16)/2 = 12 。 key \> a_{12} ,查找右半部分。
- 第 3 次比较: low = 13 , high = 16 → mid = (13 + 16)/2 = 14 。 key \> a_{14} ,查找右半部分。
- 第 4 次比较: low = 15 , high = 16 → mid = (15 + 16)/2 = 15 。 key \> a_{15} ,查找右半部分。
- 第 5 次比较: low = 16 , high = 16 → mid = 16 。 key \> a_{16} ,查找右半部分。
- 结束:此时 low \> high ,查找失败。
四 参考答案
B ✅️
五 考点精析
5.1 折半查找基本概念
1. 定义
- 折半查找(Binary Search) 是一种在有序顺序表中高效查找特定元素的算法。
- 每次将查找区间缩小一半,时间复杂度为 O(logn)O(logn) 。
2. 前提条件
- ✅ 表必须有序(升序或降序)
- ✅ 必须采用顺序存储结构(如数组),支持随机访问
- ❌ 不适用于链表(无法 O(1)O(1) 访问中间元素)
📌 应用场景:数据库索引、字典查找、算法题中的搜索优化等。
5.2 折半查找核心性质
折半查找核心性质
| 性质 | 说明 |
|---|---|
| 时间复杂度 | 最好 O(1) ,最坏/平均 O(\\log_2 n) |
| 空间复杂度 | 迭代版 O(1) ,递归版 O(\\log n) (栈空间) |
| 判定树模型 | 折半查找过程可抽象为一棵平衡二叉判定树 |
| 最大比较次数 | - 成功查找: \\lceil \\log_2(n+1) \\rceil - 失败查找: \\lfloor \\log_2 n \\rfloor + 1 |
| 稳定性 | 不涉及排序稳定性(仅查找) |
💡 关键公式记忆:
- n = 16 → 失败最多比 5 次(2010 真题)
- n = 100 → 成功最多比 7 次( \\lceil \\log_2 101 \\rceil = 7 )
5.3 折半查找算法思想
5.3.1 核心思想
- 在一个有序数组 中查找目标值
key。 - 每次将查找区间 [low, high] 缩小一半:
- 计算中间位置
mid = (low + high) / 2 - 比较 arr[mid]与 key:
- 若相等 → 查找成功,返回下标
- 若
arr[mid] > key→ 在左半区[low, mid-1]继续查找 - 若
arr[mid] < key→ 在右半区[mid+1, high]继续查找
- 计算中间位置
- 重复上述过程,直到找到目标或区间为空(
low > high)
5.3.2 前提条件
- ✅ 数组必须有序(升序或降序)
- ✅ 必须支持随机访问(如数组),不适用于链表
5.3.3 时间复杂度
- 最好情况 : O(1)O(1)O(1)(第一次就命中)
- 最坏/平均情况 : O(log2n)O(\log_2 n)O(log2n)
5.4 代码实现
5.4.1 C 语言(迭代版)
c
#include <stdio.h>
/**
* 折半查找(迭代实现)
* @param arr 有序数组
* @param n 数组长度
* @param key 要查找的关键字
* @return 找到返回下标,未找到返回 -1
*/
int binarySearch(int arr[], int n, int key) {
int low = 0;
int high = n - 1;
while (low <= high) { // 注意:是 <=,不是 <
int mid = (low + high) / 2; // 向下取整
if (arr[mid] == key) {
return mid; // 查找成功
} else if (arr[mid] > key) {
high = mid - 1; // 在左半区查找
} else {
low = mid + 1; // 在右半区查找
}
}
return -1; // 查找失败
}
// 测试示例
int main() {
int arr[] = {1, 3, 5, 7, 9, 11, 13, 15};
int n = sizeof(arr) / sizeof(arr[0]);
int key = 7;
int result = binarySearch(arr, n, key);
if (result != -1) {
printf("找到 %d,位置为 %d\n", key, result);
} else {
printf("未找到 %d\n", key);
}
return 0;
}
✅ 特点:无递归开销,空间效率高,408 推荐写法
5.4.2 Java(迭代版)
java
public class BinarySearch {
/**
* 折半查找(迭代实现)
* @param arr 有序数组
* @param key 要查找的关键字
* @return 找到返回下标,未找到返回 -1
*/
public static int binarySearch(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
// 使用无符号右移防止 (low + high) 溢出
int mid = (low + high) >>> 1;
if (arr[mid] == key) {
return mid;
} else if (arr[mid] > key) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
// 测试
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9, 11, 13, 15};
int key = 7;
int result = binarySearch(arr, key);
if (result != -1) {
System.out.println("找到 " + key + ",位置为 " + result);
} else {
System.out.println("未找到 " + key);
}
}
}
✅ 工程实践建议:使用
(low + high) >>> 1避免整数溢出
5.4.3 Python(递归版)
python
def binary_search(arr, key, low=0, high=None):
"""
折半查找(递归实现)
:param arr: 有序列表
:param key: 要查找的关键字
:param low: 查找起始下标
:param high: 查找结束下标
:return: 找到返回下标,未找到返回 -1
"""
if high is None:
high = len(arr) - 1
# 递归终止条件
if low > high:
return -1
mid = (low + high) // 2 # 整数除法,向下取整
if arr[mid] == key:
return mid
elif arr[mid] > key:
return binary_search(arr, key, low, mid - 1) # 左半区
else:
return binary_search(arr, key, mid + 1, high) # 右半区
# 测试
if __name__ == "__main__":
arr = [1, 3, 5, 7, 9, 11, 13, 15]
key = 7
result = binary_search(arr, key)
if result != -1:
print(f"找到 {key},位置为 {result}")
else:
print(f"未找到 {key}")
✅ Python 也支持迭代版,但递归更简洁;注意默认参数处理
5.5避坑指南
-
计算
mid时的溢出问题 :不要用(low + high) / 2,而要用low + (high - low) / 2,避免整数溢出。 -
循环条件 :迭代版的循环条件是
low <= high,不是low < high,否则会漏掉最后一个元素。 -
递归深度:递归版在 n 很大时可能栈溢出,实际工程中优先使用迭代版。
5.6 次数总结
5.6.1 成功和失败最大比较次数
| 查找情况 | 最大比较次数公式 | 说明 |
|---|---|---|
| 查找成功 | \\lceil \\log_2(n+1) \\rceil | 对应判定树的最大深度(内部节点) |
| 查找失败 | \\lfloor \\log_2 n \\rfloor + 1 | 对应判定树的最大外部路径长度 |
✅ 口诀 :
"成功加一取上整,失败不加取下整再加一"
六 考点跟踪
| 年份 | 题号 | 考查内容 | CSDN 参考链接 | VX参考链接 |
|---|---|---|---|---|
| 2010 | 第9题 | 折半查找失败比较次数 | ||
| 2017 | 第8题 | 折半查找判定数形状判断 | ||
| 2023 | 第8题 | 折半查找关键字比较次数最多 |
说明 :本文内容基于公开资料整理,参考了包括但不限于《数据结构》(严蔚敏)、《计算机操作系统》(汤小丹)、《计算机网络》(谢希仁)、《计算机组成原理》(唐朔飞)等国内高校经典教材,以及其他国际权威著作。同时,借鉴了王道、天勤、启航等机构出版的计算机专业考研辅导系列丛书 中的知识体系框架与典型题型分析思路。文中所有观点、例题解析及文字表述均为作者结合自身理解进行的归纳与重述,未直接复制任何出版物原文。内容仅用于学习交流,若有引用不当或疏漏之处,敬请指正。