查找算法
查找算法
查找基本概念
查找表
查找表(Search Table)是由同一类型的数据元素组成的集合。根据操作方式不同分为两类:
| 类型 | 定义 | 操作 | 典型结构 |
|---|---|---|---|
| 静态查找表 | 仅做查找操作的查找表 | 查询某元素是否存在、检索属性 | 顺序表、有序表 |
| 动态查找表 | 查找的同时进行插入/删除 | 查找时插入不存在的元素,或删除已存在的元素 | 二叉排序树、散列表、B 树 |
关键字
关键字(Key)是数据元素中某个数据项的值,用来标识一个数据元素。
- 主关键字:能唯一标识一个数据元素的关键字(如学号)
- 次关键字:能识别若干个数据元素的关键字(如姓名,可能重复)
ASL(平均查找长度)
ASL(Average Search Length)是衡量查找算法效率的核心指标,定义为查找过程中关键字比较次数的期望值:
ASL = Σ(i=1 to n) Pᵢ × Cᵢ其中 Pᵢ 是查找第 i 个元素的概率,Cᵢ 是找到第 i 个元素所需的比较次数。
易错:ASL 分为查找成功 ASL 和查找失败 ASL,两者的分母不同——成功用 n(元素个数),失败用失败区间数(通常为 n+1)。混淆分母是最常见的丢分点。
顺序查找
核心思想
从表的一端出发,逐个比较关键字,命中即停,遍历结束未找到则失败。
哨兵优化
// 顺序查找(带哨兵)int SeqSearch_Sentinel(SSTable ST, int key) { ST.elem[0] = key; // 哨兵 int i = ST.length; while (ST.elem[i] != key) i--; return i; // 返回 0 说明查找失败}哨兵优化的本质:将 elem[0] 设为目标值,循环一定会在 i == 0 时终止,从而省去每轮循环中的边界检查。条件判断次数从 2n 降至 n。
有序表的顺序查找
当表按升序排列时,可在发现当前元素大于目标值时提前终止,减少失败时的比较次数。
复杂度
| 指标 | 值 | 说明 |
|---|---|---|
| ASL 成功 | (n+1)/2 | 等概率 |
| ASL 失败(无序) | n+1 | 需比较完所有元素 |
| ASL 失败(有序) | n/2 + n/(n+1) | 可提前终止 |
| 时间复杂度 | O(n) | 平均 |
| 空间复杂度 | O(1) | 仅需辅助变量 |
折半查找(二分查找)
适用条件
有序 + 顺序存储,两个条件缺一不可。链表不能折半查找,因为无 O(1) 随机访问能力。
核心思想
每次比较中间元素,将查找区间缩小为原来的一半。
int BinarySearch(int a[], int n, int key) { int low = 0, high = n - 1, mid; while (low <= high) { mid = (low + high) / 2; if (a[mid] == key) return mid; else if (a[mid] > key) high = mid - 1; else low = mid + 1; } return -1;}判定树
折半查找的过程可以用一棵判定树描述。判定树的重要性质:
- 是一棵平衡二叉排序树,形态唯一(取决于 n 和 mid 取整方式)
mid = (low+high)/2(向下取整)时,若结点数为偶数,左子树比右子树少 1- 树高 h = ⌈log₂(n+1)⌉
- 查找成功的比较次数 = 元素在判定树中的层数
- 查找失败的比较次数 = 外部结点(空结点)的父结点层数
ASL 计算
| 指标 | 值 | 说明 |
|---|---|---|
| ASL 成功 | ≈ log₂(n+1) - 1 | 等概率 |
| ASL 失败 | ≈ log₂(n+1) | 对应外部结点平均层数 |
| 时间复杂度 | O(log₂n) | 平均和最坏均如此 |
易错:mid 的取整方式(向下取整 vs 向上取整)会构造出不同形态的判定树,导致 ASL 不同。408 默认向下取整,但真题可能指定向上取整。
为什么链表不能折半查找
折半查找需要随机访问 a[mid](O(1) 时间定位中间元素)。链表访问第 mid 个结点需要从头遍历 O(n),导致折半查找退化为比顺序查找更慢的算法。因此折半查找仅适用于顺序表(数组)。
分块查找(索引顺序查找)
核心思想
分块查找是顺序查找与折半查找的折中方案,核心性质是块间有序、块内无序:
- 块间有序:第 i 块中的所有元素均小于第 i+1 块中的所有元素
- 块内无序:每一块内部的元素可以是无序的
查找过程分两步:
- 确定目标所在的块:在索引表中查找(可用顺序或折半查找)
- 在块内顺序查找:从块起始位置逐个比较
最优分块与 ASL
设查找表长度为 n,均匀分为 b 个块,每块 s 个元素(n = b x s)。
| 查找方式 | ASL 公式 | 最优 ASL |
|---|---|---|
| 索引表顺序查找 + 块内顺序 | (b+1)/2 + (s+1)/2 | √n + 1(s = √n 时) |
| 索引表折半查找 + 块内顺序 | ⌈log₂(b+1)⌉ + (s+1)/2 | 略低于 √n + 1 |
易错:最优分块大小 s = √n 时,ASL ≈ √n + 1。很多同学忘记加 1。
树表查找
树表查找利用树结构组织数据,适合动态查找(频繁插入/删除)。相关的树表查找结构包括:
- 二叉排序树与 AVL 树:BST 利用左小右大性质,平均 O(log n);AVL 通过旋转保持严格平衡,保证 O(log n)
- 红黑树:弱平衡策略,插入最多转 2 次、删除最多转 3 次,适合频繁增删
- B 树与 B+ 树:多路平衡查找树,专为磁盘 I/O 设计,数据库索引首选
详细内容见各概念页面。
各种查找方法对比
ASL 汇总
| 查找方法 | ASL 成功 | ASL 失败 | 适用结构 |
|---|---|---|---|
| 顺序查找(无序) | (n+1)/2 | n+1 | 顺序表或链表 |
| 顺序查找(有序) | (n+1)/2 | n/2 + n/(n+1) | 有序顺序表或链表 |
| 折半查找 | ≈ log₂(n+1) - 1 | ≈ log₂(n+1) | 有序顺序表 |
| 分块查找 | ≈ √n + 1 | — | 块间有序 |
| BST/AVL 查找 | O(log n) | O(log n) | 二叉排序树 |
| B 树查找 | O(log_m n) | O(log_m n) | 多路平衡树 |
| 散列查找 | 接近 O(1) | 取决于 α | 散列表 |
场景选择建议
| 场景 | 推荐算法 | 原因 |
|---|---|---|
| 数据无序,不能排序 | 顺序查找 / 散列查找 | 不依赖有序性 |
| 数据有序,静态表 | 折半查找 | 效率高,不需增删 |
| 数据有序,需动态增删 | BST / AVL / 红黑树 | 支持动态操作 |
| 数据量极大,存于外存 | B 树 / B+ 树 | 磁盘 I/O 优化 |
| 仅需精确匹配 | 散列查找 | O(1) 平均时间 |
| 需要范围查询 | B+ 树 / AVL | 支持有序遍历 |
考研高频考点
- ASL 计算:成功和失败的区分、分母不同(选择题/填空题/大题)
- 折半查找判定树的构造与 ASL 推导(大题常考)
- 折半查找适用条件:有序 + 顺序存储,链表不行(选择题陷阱)
- 分块查找 ASL 计算、最优分块 s = √n(计算题)
- 哨兵优化的原理:比较次数从 2n 降至 n(选择题)
- 各种查找方法的 ASL 对比与场景选择(简答题)
- 静态查找表 vs 动态查找表的区分(概念题)
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!










