【算法-查找】查找算法

查找是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 的含义(程序阅读题常考)
相关推荐
星恒随风7 小时前
C++ STL 详解:set 与 multiset 的使用、区间查询和算法应用
开发语言·c++·笔记·学习·算法
Lumos1868 小时前
《ESP32 物联网全栈实战-02》ESP-IDF 工程结构
算法
SamDeepThinking8 小时前
现场重构一段代码,展示什么叫「好代码,都在表达自己的意图」
java·后端·程序员
QN1幻化引擎8 小时前
把 意识评测做成了一场"非侵入实验":不碰生产代码,分数反而更真了
人工智能·算法·架构
小园子的小菜8 小时前
Java 并发四大工具类深度解析:CountDownLatch、CyclicBarrier、Semaphore、Exchanger 原理、源码与面试考点
java·开发语言·面试
搭贝低代码8 小时前
设备管理系统迁移改造:从手工台账到二维码数字化的实践路径
java·低代码·搭贝
前端双越老师8 小时前
Nest.js 和 Spring Boot 为何如此像?写给前端转 Java 的同学
java·spring boot·全栈
JL158 小时前
Java+Go 混合架构怎么搭?收官 50 道面试题 + 学习路线
java·架构·golang
冻柠檬飞冰走茶8 小时前
PTA基础编程题目集 7-17 爬动的蠕虫(C语言实现)
c语言·开发语言·数据结构·算法
Tattoo_Welkin8 小时前
IDEA 中常用操作记载
java·elasticsearch·intellij-idea