查找算法

1793 字
9 分钟
查找算法

查找算法#

查找基本概念#

查找表#

查找表(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 块中的所有元素
  • 块内无序:每一块内部的元素可以是无序的

查找过程分两步:

  1. 确定目标所在的块:在索引表中查找(可用顺序或折半查找)
  2. 在块内顺序查找:从块起始位置逐个比较

最优分块与 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)/2n+1顺序表或链表
顺序查找(有序)(n+1)/2n/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支持有序遍历

详细内容请见 散列表B 树与 B+ 树

考研高频考点#

  • ASL 计算:成功和失败的区分、分母不同(选择题/填空题/大题)
  • 折半查找判定树的构造与 ASL 推导(大题常考)
  • 折半查找适用条件:有序 + 顺序存储,链表不行(选择题陷阱)
  • 分块查找 ASL 计算、最优分块 s = √n(计算题)
  • 哨兵优化的原理:比较次数从 2n 降至 n(选择题)
  • 各种查找方法的 ASL 对比场景选择(简答题)
  • 静态查找表 vs 动态查找表的区分(概念题)

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

查找算法
https://lingluoa.icu/posts/search-algorithms/
作者
lingluoa
发布于
2026-07-07
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lingluoa
Hello, I'm lingluoa.
公告
欢迎来到我的博客!不定期更新中。
文章目录
标签
站点统计
文章
53
分类
9
标签
75
总字数
175,771
运行时长
0
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.15.6
文章许可
CC BY-NC-SA 4.0