【算法-查找】查找算法

查找是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 的含义(程序阅读题常考)
相关推荐
海石1 小时前
子树怎么找?树的3种遍历方式来帮忙!
算法·leetcode
海石1 小时前
难度分 1588:思路 + 技巧 = AC
算法·leetcode
HZZD_HZZD3 小时前
DL/T 645-2026新国标深度解读与智能电表协议适配实战:从帧结构变化到Java采集器升级的全链路改造方案
java·开发语言
珠海西格电力5 小时前
云边端协同架构:零碳园区管理系统的技术底座
大数据·运维·人工智能·算法·架构·能源
山登绝顶我为峰 3(^v^)36 小时前
C/C++ 交叉编译方法
java·c语言·c++
一叶飘零_sweeeet6 小时前
Codex 与 Claude Code 深度拆解:两代 AI 编程智能体的技术本质与 Java 实战指南
java·ai·codex·claude code
Listen·Rain6 小时前
提示词和提示词模板
java·人工智能·spring boot
还有多久拿退休金7 小时前
让飞书知识库跟着 commit 自己长:一套自动化知识库的真实实现
前端·算法·架构