一、概念
1、基本术语概念
- 【三个术语】
- 【查找】、【查找表】、【关键字】
- 【两种基本操作】
- 【只查不改】or【又查又改】
2、【查找算法】的【评估指标】
1)【ASL(平均查找次数)】
- 由于我之前在【操作系统:文件系统(文件逻辑结构)】部分研究【顺序文件的平均查找次数】时写过笔记,因此不再过多描述,我个人的笔记如下图所示:
2)【查找成功】和【查找失败】两种情况下的ASL
- 【查找成功】的【平均查找次数ASL】
- 【查找失败】的【平均查找次数ASL】
二、顺序查找
1、简单解释算法
2、代码理解
- 代码非常简单啊,写过代码的都会写
- ★【哨兵模式】对比:注意还有一种算法是【哨兵模式】,就是把关键字放在【数组第0位】,从后往前对比,每个元素都和哨兵对比,直到找到、或者i到哨兵的位置
3、【算法评估指标:ASL】
- 成功的ASL :因为查每个元素概率都一样是1/n,所以就是【(1+2+3+...+n/n)】=【n+1/2】
- 失败的ASL :不用算了,因为只要关键字查找失败,那必然是要遍历整个数组【n】
【哨兵模式下】
- 成功的ASL :一样
- 失败的ASL :n+1(因为数组多了哨兵这1位元素要遍历)
4、【优缺点】
5、【有序】的【顺序查找】
记住:【有序顺序查找】比【乱序的顺序查找】唯一牛逼的地方只体现在【查找失败】
【例题】
三、折半查找
1、解释算法逻辑
首先只能适用于【有序 的 顺序表】
- 就是二分查找,设置【左区间下限low】、【中间值mid】、【右区间上限high】
- 每次对比查找时,【关键字】和【mid】去对比
- 【小于mid】就说明在【左区间】
- 那么就只用去左边找,把【右边上限high】缩小到【mid的左边区间】
- 【大于mid】就说明在【右区间】
- 那么就只用去右边找,把【左边上限low】放大到【mid的右边区间】
- 【等于mid】就说明刚好【找到了!!!】
- mid每次区间变化后,【mid】都更新为【low+high / 2 (注意:向下取整)】
- 记住:【查找成功是:关键字 = mid】、【查找失败是:low > high(倒反天罡)】
2、代码理解
3、【算法评估ASL】和【折半查找判定树】
- 【折半查找判定树】
![]()
- 每个左、右区间都可以进一步把【mid】作为【子树的根节点】
- 又分出一棵子树,逐渐构成一整个完整树
- 【折半查找判定树】性质分析
- 1)首先直到因为【mid的向下取整的性质】
- 所以【偶数时:左区间少于右区间1个】、【奇数时:左右区间一样】
- 而且说明【右子树节点数 和 左子树节点数 之差是:0或1】
- 【重视这个知识点】:可以快速判断一棵树是否符合【折半查找树】
- 那么【左子树和右子树深度只差不超过1】
- 所以**【折半查找树】必然是【平衡二叉树】** (而【平衡二叉树】又类似于【完全二叉树】)
- 2)【查找成功的情况】
- 该折半查找树的必然是一个平衡二叉树,可以发现:查【第i层某个数】刚好就是【查i次】(比如查到13,先查第一次29、再到第二层13,共两次)
- 所以,假设该折半查找树有m层、n个节点
- 【平均查找次数ASL】=【1*1+2*2+...+m*m】/【n】
- 3)【查找失败的情况】
- 可以发现:查失败必然是【每条路径左 / 右走到头】,也就是【该折半查找树的空指针域】
- 所以,假设该折半查找树有n个节点
- 则会有**【n+1】个【空链域】** ,也就是**【n+1】个【失败节点】**
- 所以: 【平均查找失败次数ASL】=【最少查找失败次数*n-1层失败节点数 + 最多查找失败次数*n层失败节点数】/【n+1(失败节点数)】
- 例子:
- 4)折半查找树的【树高】
- 【n个节点的平衡二叉树】的最大高度是:【log2(n+1)】
![]()
- 【回顾完全二叉树高度h】
- 5)时间复杂度
- 已知树高是【log2(n+1)】
- 而无论【查找成功次数】、还是【查找失败次数】最多都【不会超过树高】
- 所以按时间复杂度计量:【log2(n+1)】的数量级是【O(log2(n+1))】
- 【但是注意】:
- 并非任何情况下【折半查找】永远比【顺序查找】更快
- 在【关键字就在数组首位】的情况,明显【顺序查找】更快
- 【还有一个重点】!!!!
- 【折半查找树】的【中序遍历】一定是【升序】
![]()
- 【例题】
【例题】
- 注意:老是忘记的话就这么记忆
四、分块查找
1、基本概念理解
- 用【索引表】记录数组里的【大小不等的、连续的块】
- 而这些【大小不等的、连续的块】里才是【关键字要对比的元素】
- 【块之间:有序】、【块内:无序】
(联想到计组、操作系统里的内存、磁盘分块,那些是反过来:块之间无序,块内必须连续)
2、解释算法逻辑
1)顺序分块查找
- 【索引表关键字】 记录的元素是数组【每个块里的 "最大元素"】
- (注意:而且索引表的关键字 、数组的元素 都必须是 升序排列 )
![]()
- 那么关键字顺序遍历索引表,只用比较自己是否大于这个【索引表元素】
- 如果【大于索引表元素】,说明【不在这个块】;
- 如果【小于等于这个索引表元素】,说明【在这个块】
- 那么找到【这个块内】的【第1位元素位置】
- 接着顺序遍历块内,直到找到元素,查找成功;
- 如果【在这个块内找不到,从而超出该分块范围去查找】就说明该数组查找失败!
2)折半分块查找
- 因为分块查找是【按顺序排列】的,那自然也可以利用【折半查找】对其升级!
- 【查找成功】
- 在【索引表关键字对比】时就【查找成功】
- 在【索引表关键字对比】时【查找失败】,但在【数组块内查找成功】
![]()
- 分析:【为什么要在low所指的块内查找】
- 如果此时一直在索引表查找,索引表查找失败的标志是【low>high】
- 然而在快失败之前一步,必然是【mid=low=high】,三指针并在一起
- 如果【关键字 > mid】,说明应该在【mid右边查】,low会左移1位,此时【low > high】
- 而【mid = (low + high) / 2】必然是偏向左边,也就是原本【mid=low=high】位置,可是刚刚已经对比过了【关键字大于那】
- 所以只可能是在【右移后的low】所在的块,如果还找不到,那就是真实查找失败了
- 【查找失败】
- 而如果**【low】直接跑出索引表外了,那100%查找失败了**
3、【算法评估指标:ASL】
我用最直白、最不绕弯子的方式告诉你:
- 就是【2个数组的顺序查找ASL相加】:
- 【索引表的顺序查找ASL】+【该数组块内顺序查找ASL】
- 【ASL最小情况】
- 【S】=【数组分成几块】=【块内数组长度】
- 【S】=【根号n(n是整个数组长度)】时,ASL最小
- 【导数证明】
- 【基本不等式证明】
- 字面理解:
- 就是把【长度n的数组】平均分成【n个数组分块】
- 这n个【数组分块】里面,每块又分成【n个元素】
- 联想:《操作系统------文件系统之逻辑结构》里的索引表分组!!!
【例题】
【小难】
























































(联想到计组、操作系统里的内存、磁盘分块,那些是反过来:块之间无序,块内必须连续)














