【算法-查找】查找算法

查找是CSP-J初赛必考 的内容,每年至少1-2题,总分值约4-6分 。下面我从顺序查找→二分查找→插值查找→哈希查找,系统讲解所有考点。

一、查找算法总览

算法 时间复杂度 前提条件 空间复杂度 CSP-J考频
顺序查找 O(n) 无 O(1) ★★★★★
二分查找 O(log n) 有序 O(1) ★★★★★
插值查找 O(log log n)~O(n) 有序且均匀分布 O(1) ★
哈希查找 O(1)平均 哈希表 O(n) ★★

二、顺序查找(线性查找)

2.1 基本思想

从数组的第一个元素开始,逐个比较,直到找到目标或遍历完整个数组。

2.2 代码模板

cpp 复制代码
int seqSearch(int a[], int n, int x) {
    for (int i = 0; i < n; i++) {
        if (a[i] == x) return i;  // 找到返回下标
    }
    return -1;  // 未找到
}

2.3 特点分析

特性 说明
时间复杂度(最好) O(1) ------ 第一个元素就是目标
时间复杂度(最坏) O(n) ------ 目标在末尾或不存在
时间复杂度(平均) O(n)
空间复杂度 O(1)
适用条件 无任何要求,数组可以无序
稳定性 不涉及

2.4 典型例题

题目: 在数组 [8, 3, 5, 9, 2, 7, 1] 中查找元素 7,顺序查找需要比较( )次。

A. 3  B. 4  C. 5  D. 6

答案:D

解析: 依次比较:

  • 8≠7(第1次)
  • 3≠7(第2次)
  • 5≠7(第3次)
  • 9≠7(第4次)
  • 2≠7(第5次)
  • 7=7(第6次,找到)

所以需要比较6次,选D。


三、二分查找(折半查找)★★★★★

3.1 基本思想

在有序数组中,每次取中间元素与目标比较:

  • 相等 → 找到
  • 目标 < 中间值 → 在左半部分继续找
  • 目标 > 中间值 → 在右半部分继续找

3.2 代码模板

写法1:闭区间 l, r

cpp 复制代码
int binarySearch(int a[], int n, int x) {
    int l = 0, r = n - 1;
    while (l <= r) {                // 注意:l <= r
        int mid = l + (r - l) / 2;  // 防溢出写法
        if (a[mid] == x) return mid;
        else if (a[mid] < x) l = mid + 1;
        else r = mid - 1;
    }
    return -1;
}

写法2:左闭右开 [l, r)

cpp 复制代码
int binarySearch(int a[], int n, int x) {
    int l = 0, r = n;
    while (l < r) {                 // 注意:l < r
        int mid = l + (r - l) / 2;
        if (a[mid] == x) return mid;
        else if (a[mid] < x) l = mid + 1;
        else r = mid;
    }
    return -1;
}

3.3 常见变形

查找第一个 ≥ x 的位置(lower_bound):

cpp 复制代码
int lowerBound(int a[], int n, int x) {
    int l = 0, r = n;
    while (l < r) {
        int mid = l + (r - l) / 2;
        if (a[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return l;  // 第一个 >= x 的位置
}

查找第一个 > x 的位置(upper_bound):

cpp 复制代码
int upperBound(int a[], int n, int x) {
    int l = 0, r = n;
    while (l < r) {
        int mid = l + (r - l) / 2;
        if (a[mid] > x) r = mid;
        else l = mid + 1;
    }
    return l;  // 第一个 > x 的位置
}

3.4 特点分析

特性 说明
时间复杂度(最好) O(1) ------ 第一次就找到
时间复杂度(平均/最坏) O(log₂n)
空间复杂度(迭代) O(1)
空间复杂度(递归) O(log n)(递归栈)
前提条件 数组必须有序(升序或降序)
最坏比较次数 ⌊log₂n⌋ + 1

3.5 查找次数计算

公式: 在n个元素的有序数组中二分查找,最多需要比较 ⌊log₂n⌋ + 1 次。

n ⌊log₂n⌋ + 1 举例
1 1 1个元素
2-3 2 3个元素最多2次
4-7 3 7个元素最多3次
8-15 4 15个元素最多4次
16-31 5 31个元素最多5次

记忆口诀: "2的k次方减1个元素,最多k次"

3.6 典型例题

例题1(基础查找过程)

题目: 在有序数组 [1, 3, 5, 7, 9, 11, 13] 中查找 9,二分查找过程中 mid 的取值依次是( )

A. 4, 6, 5  B. 3, 5, 4  C. 4, 5, 6  D. 3, 4, 5

答案:B

解析:

  • 初始 l=0, r=6, mid=3(a3=7)
  • 9 > 7,去右半:l=4, r=6, mid=5(a5=11)
  • 9 < 11,去左半:l=4, r=4, mid=4(a4=9,找到)

mid取值:3, 5, 4

选B。


例题2(查找次数)

题目: 在 100 个元素的有序数组中,二分查找最多需要比较( )次。

A. 6  B. 7  C. 8  D. 9

答案:B

解析: 最多比较次数 = ⌊log₂100⌋ + 1

  • 2⁶ = 64,2⁷ = 128
  • ⌊log₂100⌋ = 6
  • 最多 = 6 + 1 = 7 次

选B。


例题3(lower_bound 变形)

题目: 在有序数组 [1, 3, 5, 7, 7, 7, 9] 中,用 lower_bound 找第一个 ≥ 7 的位置是( )

A. 2  B. 3  C. 4  D. 5

答案:B

解析: lower_bound 返回第一个 ≥ x 的位置。

数组索引:0:1, 1:3, 2:5, 3:7, 4:7, 5:7, 6:9

第一个 ≥ 7 的位置是索引 3。

选B。


例题4(递归二分)

题目: 以下递归二分查找的时间复杂度是( )

cpp

复制代码
int bs(int a[], int l, int r, int x) {
    if (l > r) return -1;
    int mid = (l + r) / 2;
    if (a[mid] == x) return mid;
    if (a[mid] > x) return bs(a, l, mid-1, x);
    return bs(a, mid+1, r, x);
}

A. O(n)  B. O(log n)  C. O(n log n)  D. O(n²)

答案:B

解析: 每次将搜索范围缩小一半,递归深度 O(log n),每次 O(1),总 O(log n)。


四、插值查找(了解即可)

4.1 基本思想

二分查找总是取中间位置,而插值查找根据目标值在有序数组中的位置比例来估算mid。

公式:

text

复制代码
mid = l + (r - l) * (x - a[l]) / (a[r] - a[l])

4.2 特点

特性 说明
适用场景 数据有序且均匀分布(如 1,2,3,4,5,6...)
最好时间复杂度 O(log log n)
最坏时间复杂度 O(n)
与二分对比 分布均匀时比二分快,不均匀时可能退化

CSP-J要求: 只要知道插值查找存在,知道它是二分查找的改进版即可,不要求手写代码。


五、哈希查找(Hash)

5.1 基本思想

通过哈希函数将关键字映射到表中的位置,直接访问。

5.2 核心概念

概念 说明
哈希函数 将关键字映射为数组索引
冲突 两个不同关键字映射到同一位置
负载因子 元素个数 / 表长度
拉链法 同一位置的元素用链表存储
开放地址法 冲突时找下一个空位置

5.3 特点

特性 说明
平均时间复杂度 O(1)
最坏时间复杂度 O(n)(大量冲突)
空间复杂度 O(n)
适用条件 需要设计哈希函数和解决冲突

5.4 典型例题(哈希查找)

题目: 哈希表中,装填因子(负载因子)越大,下列说法正确的是( )

A. 查找效率越高  B. 冲突概率越大

C. 空间利用率越低  D. 冲突概率越小

答案:B

解析: 装填因子 = 元素个数 / 表长度。装填因子越大,说明表越"满",冲突概率越大,查找效率越低。

选B。


六、查找算法对比表

对比项 顺序查找 二分查找 哈希查找
时间复杂度(平均) O(n) O(log n) O(1)
时间复杂度(最坏) O(n) O(log n) O(n)
空间复杂度 O(1) O(1) O(n)
数据要求 无 有序 需哈希函数
优缺点 简单但慢 快但要求有序 极快但需额外空间

七、程序阅读题中的查找算法识别

特征识别表

代码特征 算法
for(i=0; i<n; i++) if(a[i]==x) 顺序查找
while(l<=r){ mid=(l+r)/2; ... } 二分查找
mid = l + (r-l)*(x-a[l])/(a[r]-a[l]) 插值查找
hash[key] = value 哈希查找

八、二分查找的易错点与陷阱

陷阱1:边界条件错误

cpp 复制代码
while (l < r) { ... }      // 适用左闭右开 [l, r)
while (l <= r) { ... }     // 适用闭区间 [l, r]

陷阱2:mid 计算溢出

cpp 复制代码
int mid = (l + r) / 2;          // l+r 可能超过 int 范围
int mid = l + (r - l) / 2;      // ✅ 安全写法

陷阱3:死循环

cpp 复制代码
while (l < r) {
    mid = (l + r) / 2;
    if (a[mid] < x) l = mid;    // ❌ 可能死循环
    else r = mid;
}
// 正确写法:l = mid + 1

陷阱4:数组未排序

二分查找的大前提是数组有序。如果数组未排序,二分查找结果错误。


九、查找算法知识思维导图

复制代码
                    查找算法
                       ↓
        ┌──────────────┼──────────────┐
        ↓              ↓              ↓
    顺序查找        二分查找       哈希查找
    无要求          有序数组       哈希函数
    O(n)            O(log n)       O(1)平均
        ↓              ↓              ↓
    遍历比较       lower_bound   拉链法/开放地址
                  upper_bound   冲突处理
                       ↓
                 递归/迭代两种实现

十、实战检测(8题自测)

题号 题目 你的答案
1 在数组 [2,5,8,12,16,23,38,56,72,91] 中二分查找 23,mid 的取值依次是?
2 1024 个元素的有序数组,二分查找最多需要比较几次?
3 顺序查找的时间复杂度是?
4 哈希查找的平均时间复杂度是?
5 lower_bound 返回的是?
6 二分查找的前提条件是?
7 int mid = (l + r) / 2 的潜在风险是?
8 插值查找适合什么类型的数据?
  1. 4, 6, 5(a4=16→右,a6=38→左,a5=23找到)
  2. 11次(2¹⁰=1024,⌊log₂1024⌋+1=10+1=11)
  3. O(n)
  4. O(1)
  5. 第一个 ≥ x 的位置
  6. 数组必须有序
  7. l+r 可能超出 int 范围(溢出)
  8. 有序且均匀分布的数据

最后建议: 查找算法中,顺序查找 和二分查找是必考内容,必须熟练掌握。哈希查找了解基本概念即可。重点关注:

  1. 二分查找的边界条件(l <= r 还是 l < r)
  2. mid 的计算方式(防溢出写法)
  3. 查找次数的计算(⌊log₂n⌋ + 1)
  4. lower_bound / upper_bound 的含义(程序阅读题常考)
相关推荐
考虑考虑8 小时前
JDK26中的Map.ofLazy
java·后端·java ee
鶴哥只手遮天11 小时前
Spring AI 2.0.1 深度解析-第 14 章 MCP 服务端(上):注解式实现与工具来源
java·人工智能·开源
鶴哥只手遮天11 小时前
Spring AI 2.0.1 深度解析-第 15 章 MCP 服务端(下):三种协议形态、Streamable 传输与状态管理
java·人工智能·开源
鶴哥只手遮天11 小时前
Spring AI 2.0.1 深度解析-第 16 章 向量存储(上):`VectorStore` 接口体系、`BatchingStrategy`
java·人工智能·开源
鶴哥只手遮天11 小时前
Spring AI 2.0.1 深度解析-第 12 章 工具循环的流式形态、Usage 累加与扩展体系
java·人工智能·开源
鶴哥只手遮天11 小时前
Spring AI 2.0.1 深度解析-第 23 章 模型供应商实现:从 `OpenAiSetup` 到 Bedrock
java·人工智能·开源
鶴哥只手遮天11 小时前
Spring AI 2.0.1 深度解析-第 06 章 内置 Advisor 全景:13 个实现逐个剖到源码级
java·人工智能·开源
鶴哥只手遮天12 小时前
Spring AI 2.0.1 深度解析-第 03 章 消息体系与 Prompt:不可变模型、`ChatOptions` 与模板渲染
java·人工智能·开源
鶴哥只手遮天12 小时前
Spring AI 2.0.1 深度解析-第 24 章 生产实践与 2.x 迁移:陷阱清单、AOT 与调优
java·人工智能·开源
鶴哥只手遮天12 小时前
Spring AI 2.0.1 深度解析-第 10 章 工具调用(中):执行链路、结果转换与工具限制
java·人工智能·开源