408真题解析-2010-9-数据结构-折半查找的比较次数

一 真题2010-9

2010-09. 已知一个长度为 16 的顺序表 LL ,其元素按关键字有序排列。若采用折半查找法查找一个 LL 中不存在的元素,则关键字的比较次数最多的是( )。

A. 4

B. 5

C. 6

D. 7

二 题目要素解析

核心概念折半查找(Binary Search)

  • 适用条件:有序的顺序表(数组)。
  • 基本思想:将待查关键字与表中间位置的元素比较,若相等则查找成功;若不等,则缩小查找范围(前半部分或后半部分),重复上述过程。

查找判定树

  • 折半查找的过程可以用一棵二叉排序树来描述,称为判定树。
  • 树中每个节点代表表中的一个元素。
  • 树的高度 h 决定了最多比较次数。

失败节点

  • 查找失败的情况对应判定树中的外部节点(空节点)
  • 查找失败时的比较次数 = 从根节点到对应外部节点路径上的内部节点个数

三 哔哔详解

方法一:公式计算法(推荐)

对于长度为nnn 的有序表,折半查找的判定树高度 hhh 为:

h=⌊log⁡2n⌋+1h= \lfloor \log_2 n \rfloor +1h=⌊log2n⌋+1

本题中 n=16n=16n=16:

  1. 计算 log216=4log_2 16=4log216=4。
  2. 所以判定树的高度 h=4+1=5h=4+1=5h=4+1=5。
  3. 这意味着,查找成功时最多比较 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(log⁡n)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(log⁡2n)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题 折半查找关键字比较次数最多

说明 :本文内容基于公开资料整理,参考了包括但不限于《数据结构》(严蔚敏)、《计算机操作系统》(汤小丹)、《计算机网络》(谢希仁)、《计算机组成原理》(唐朔飞)等国内高校经典教材,以及其他国际权威著作。同时,借鉴了王道、天勤、启航等机构出版的计算机专业考研辅导系列丛书 中的知识体系框架与典型题型分析思路。文中所有观点、例题解析及文字表述均为作者结合自身理解进行的归纳与重述,未直接复制任何出版物原文。内容仅用于学习交流,若有引用不当或疏漏之处,敬请指正。

相关推荐
源代码•宸2 小时前
Leetcode—404. 左叶子之和【简单】
经验分享·后端·算法·leetcode·职场和发展·golang·dfs
WBluuue2 小时前
数据结构与算法:dp优化——优化尝试和状态设计
c++·算法·leetcode·动态规划
im_AMBER2 小时前
Leetcode 105 K 个一组翻转链表
数据结构·学习·算法·leetcode·链表
sin_hielo2 小时前
leetcode 1877
数据结构·算法·leetcode
睡不醒的kun3 小时前
定长滑动窗口-基础篇(2)
数据结构·c++·算法·leetcode·职场和发展·滑动窗口·定长滑动窗口
庄小焱3 小时前
【机器学习】——房屋销售价格预测实战
人工智能·算法·机器学习·预测模型
黎雁·泠崖3 小时前
Java字符串高阶:底层原理深剖+经典面试题全解
java·开发语言
txzrxz3 小时前
单调栈详解(含题目)
数据结构·c++·算法·前缀和·单调栈
重生之我是Java开发战士3 小时前
【Java SE】反射、枚举与Lambda表达式
java·开发语言