408数据结构第7章:查找①——顺序查找、折半查找、分块查找知识总结

本文对应你当前看的这一部分:

7.1 查找基本概念 → 7.2 顺序查找、折半查找、分块查找

重点不是背代码,而是弄清:适用条件、查找过程、ASL、判定树、复杂度和三种方法的区别。


一、先建立整体框架

这部分实际上只有三种思路:

复制代码
顺序查找:一个一个找

折半查找:每次砍掉一半

分块查找:先找哪一块,再到块内找

对应效率大致是:

复制代码
顺序查找:O(n)

折半查找:O(log n)

分块查找:介于二者之间

但它们的适用条件不同,不能只看复杂度。


二、查找的几个基本概念

1. 查找表

用于查找的数据集合称为:

复制代码
查找表

查找结果通常分成:

复制代码
查找成功
查找失败

2. 关键字

能够标识数据元素的数据项称为:

复制代码
关键字 key

如果一个关键字可以唯一标识一个元素,就叫:

复制代码
主关键字

3. 静态查找表和动态查找表

静态查找表只进行:

复制代码
查询

典型:

复制代码
顺序查找
折半查找

动态查找表还会进行:

复制代码
插入
删除

后面会涉及:

复制代码
二叉排序树
AVL树
B树
散列表

你当前这一部分主要还是:

复制代码
静态查找

三、平均查找长度 ASL

ASL = Average Search Length,平均查找长度。

定义:

复制代码
ASL = Σ(Pi × Ci)

其中:

复制代码
Pi:查找第i个元素的概率

Ci:找到第i个元素所需的关键字比较次数

如果所有元素等概率:

复制代码
Pi = 1/n

则:

复制代码
ASL = 所有元素比较次数之和 / n

做ASL题时不要先背公式,先问:

复制代码
每个元素到底比较几次?

四、顺序查找

核心思想:

复制代码
从一端开始,一个一个比较。

若相等:

复制代码
查找成功

扫完整张表仍找不到:

复制代码
查找失败

五、为什么顺序查找常设置"哨兵"

典型写法:

复制代码
ST.elem[0] = key

然后从末尾往前找:

复制代码
for(i = n; ST.elem[i] != key; --i);

这样不用在循环中反复判断:

复制代码
i是否越界

作用:

复制代码
减少边界判断

注意:

复制代码
哨兵不是正常数据元素。

六、一般线性表顺序查找的ASL

假设:

复制代码
n个元素
从后往前查
各元素等概率

成功比较次数可能是:

复制代码
1,2,3,...,n

所以:

复制代码
ASL成功
= (1+2+...+n)/n
= (n+1)/2

结论:

复制代码
顺序查找成功ASL = (n+1)/2

如果采用你教材中的哨兵写法,查找失败时会比较:

复制代码
n个真实元素 + 1次哨兵

所以:

复制代码
ASL失败 = n+1

注意:不同教材是否把哨兵比较算进去可能不同;按你当前教材体系记 n+1


七、有序表的顺序查找

如果表按关键字升序排列,例如:

复制代码
10 20 30 40 50

查找:

复制代码
25

扫描到:

复制代码
30 > 25

即可判断:

复制代码
25不可能在后面

所以可以提前失败。

关键区别:

复制代码
无序顺序查找:
失败通常要扫完整表

有序顺序查找:
遇到第一个大于key的元素即可失败

八、折半查找:这部分的绝对重点

折半查找又叫:

复制代码
二分查找

核心思想:

复制代码
每次拿当前查找区间的中间元素与key比较

升序表中:

复制代码
key == mid
-> 成功

key < mid
-> 去左半区

key > mid
-> 去右半区

每比较一次:

复制代码
查找范围大约减半

所以:

复制代码
时间复杂度 O(log n)

九、折半查找的两个硬条件

必须同时满足:

复制代码
① 元素按关键字有序

② 能随机访问中间位置

因此典型适用:

复制代码
有序数组 / 有序顺序表

不适合:

复制代码
链表

即使链表有序,也无法:

复制代码
O(1)直接访问第mid个元素

所以口诀:

复制代码
折半查找 = 有序 + 随机访问

十、折半查找标准过程

复制代码
low = 0
high = n-1

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
}

查找失败:

复制代码
low > high

十一、mid向上还是向下取整

常见代码:

复制代码
mid = (low+high)/2

整数除法通常向下取整。

理论上也可以规定:

复制代码
向上取整

但要求:

复制代码
整棵折半判定树必须使用同一种取整规则

不能某个子区间向上,另一个子区间又向下。


十二、折半查找判定树

折半查找过程可以用:

复制代码
判定树

表示。

树中真实结点对应:

复制代码
有序表中的关键字

而:

复制代码
结点所在层数 = 查找到该元素所需比较次数

例如:

复制代码
根结点 -> 比较1次
第二层 -> 比较2次
第三层 -> 比较3次

十三、判定树的性质

1. 是二叉排序树

满足:

复制代码
左 < 根 < 右

因此中序遍历:

复制代码
得到原来的有序序列

2. 基本平衡

因为每次都是对半分。

对n个元素:

复制代码
树高 h = ceil(log2(n+1))

等价地,最大比较次数:

复制代码
ceil(log2(n+1))

也常写:

复制代码
floor(log2 n)+1

3. n个元素有n+1个失败位置

例如:

复制代码
10 20 30

查找失败可能落在:

复制代码
(-∞,10)
(10,20)
(20,30)
(30,+∞)

因此:

复制代码
n个关键字
-> n+1个失败区间

十四、折半查找成功ASL

最稳的方法:

复制代码
按判定树层数统计

例如7个结点构成满三层:

复制代码
第1层:1个
第2层:2个
第3层:4个

则:

复制代码
ASL成功
= (1×1 + 2×2 + 3×4) / 7
= 17/7

通用:

复制代码
ASL成功
= Σ(层数 × 该层真实结点数) / n

十五、折半查找失败ASL

查找失败会落到:

复制代码
失败结点 / 外部结点

若失败结点位于第k层,那么实际完成的是:

复制代码
k-1次真实关键字比较

因此失败ASL本质上是:

复制代码
各失败位置所需真实关键字比较次数的平均值

强化阶段更常考:

复制代码
最多比较几次

而不是复杂的失败ASL手算。


十六、最大比较次数

直接记:

复制代码
最大比较次数
=
判定树高度
=
ceil(log2(n+1))

例如:

复制代码
n = 16

则:

复制代码
ceil(log2 17) = 5

最多比较:

复制代码
5次

十七、折半查找"比较序列"怎么判断

例如比较过程:

复制代码
500 -> 200 -> 450

从500到200说明:

复制代码
key < 500

从200到450说明:

复制代码
key > 200

所以此时key必须在:

复制代码
200 < key < 500

以后新的比较值必须仍符合当前区间。

所以这类题的本质是:

复制代码
不断缩小key的合法取值区间

十八、分块查找

分块查找又叫:

复制代码
索引顺序查找

它把数据分成若干块。

要求:

复制代码
块内可以无序

块间必须有序

然后建立索引表。


十九、索引表记录什么

通常记录:

复制代码
每块最大关键字
+
该块起始地址

查找分两步:

复制代码
第1步:查索引表,确定目标在哪一块

第2步:到块内做顺序查找

所以:

复制代码
分块查找 = 索引查找 + 块内查找

二十、分块查找ASL

假设:

复制代码
n个元素
分成b块
每块s个

n = b×s

若:

复制代码
索引表顺序查找
块内也顺序查找

则:

复制代码
索引查找ASL = (b+1)/2

块内查找ASL = (s+1)/2

所以:

复制代码
ASL
= (b+1)/2 + (s+1)/2

因为:

复制代码
b = n/s

得到:

复制代码
ASL
= (n/s + s + 2)/2

二十一、最优块大小

要让:

复制代码
n/s + s

最小,最优时:

复制代码
s = √n

同时:

复制代码
b = √n

因此:

复制代码
每块元素数 ≈ √n

此时:

复制代码
ASL最小 ≈ √n + 1

二十二、三种查找方法对比

方法 是否要求有序 是否要求随机访问 复杂度 特点
顺序查找 O(n) 最通用
折半查找 O(log n) 快,但条件严格
分块查找 块间有序 依赖索引 约O(√n) 折中

二十三、最容易错的地方

1. 有序就一定能折半

错。

还必须:

复制代码
能随机访问

所以:

复制代码
有序链表也不能直接折半

2. 分块查找要求块内有序

错。

正确:

复制代码
块内可以无序
块间必须有序

3. ASL就是时间复杂度

不是。

复制代码
ASL:平均比较次数

时间复杂度:增长数量级

4. 折半判定树就是任意平衡BST

不完全对。

它还必须符合:

复制代码
每次mid的选取规则

二十四、最后只记三句话

复制代码
顺序:
一个一个找。

折半:
有序 + 随机访问,每次砍一半。

分块:
先找块,再进块内找。

再配三条公式:

复制代码
顺序成功ASL = (n+1)/2

折半最大比较次数 = ceil(log2(n+1))

分块顺序+顺序的最优块大小 = √n
相关推荐
ShineWinsu1 小时前
对于C++中unordered_map的详细介绍
数据结构·c++·算法·面试·stl·哈希表·unordered_map
lingran__2 小时前
C++ 高阶数据结构:AVL 树万字详解|原理、四种旋转、key‑value 实现、调试校验、set/map/红黑树 底层前置知识
数据结构·c++·二叉搜索树·平衡二叉树·avl树·红黑树前置
疯狂打码的少年3 小时前
【数据结构】顺序表 vs 链表的对比与选择
数据结构·笔记·链表
wabs66613 小时前
关于图论【最短路径之Dijkstra算法(堆优化版)|卡码网47.参加科学大会的思考】
数据结构·算法·图论·优先级队列·邻接表·小顶堆·卡码网
yyy(十一月限定版)17 小时前
016【入门】双端队列
数据结构·c++
白狐_79817 小时前
408数据结构:中缀转后缀与操作符栈容量——真题精讲
前端·javascript·数据结构
疯狂打码的少年18 小时前
【数据结构】链表变体:双向链表与循环链表
数据结构·笔记·链表
白狐_79818 小时前
408数据结构第5章:树与二叉树③——公式分类速查
数据结构·分类·数据挖掘
Iruoyaoxh18 小时前
数据结构排序
数据结构