查找的基本概念
基本概念
对查找表的常见操作
查找算法的评价指标
顺序查找
算法思想
算法实现
普通版
哨兵版
- 当我们要查找某个关键字的时候会把这个关键字放到零号位置的这个地方,也就是所谓的哨兵
查找效率分析
顺序查找算法的优化
对有序表
- 上面的树型结构称为
查找判定树
用查找判定树分析ASL
被查概率不相等
折半查找
- 又称为二分查找
算法思想
代码实现
查找效率分析
折半查找判定树的构造
- 1个结点
- 2个结点
- 3个结点
- 4个结点
- 5个结点
- 6个元素
- 7个元素
- 8个元素
- 9个元素
- 10个元素
- 11个元素
- 12个元素
- 13个元素
- 14个元素
- 15个元素
- 16个元素
查找效率
拓展思考
分块查找
- 又称为索引顺序查找
算法思想
用折半查找查索引
拓展思考
二叉排序树BST
二叉排序树的定义
二叉排序树的查找
二叉排序树的插入
二叉排序树的构造
二叉排序树的删除
查找效率分析
平衡二叉树AVL
平衡二叉树的定义
平衡二叉树的插入
- 我们把最小的不平衡子树恢复平衡之后,其他的祖先结点的平衡因子也都恢复了原来正常值
调整最小不平衡子树
LL
- 如果A的右子树AR高度不是H而是H+1,则在B左子树BL插入新结点之后A的平衡因子为1,仍是平衡的,不会出现不平衡的情况
- 如果A的右子树AR的高度为H-1,那么A本来就是不平衡的,也明显不对,我们此时要探讨的是插入新结点之后才导致不平衡的这样一种情况
- 如果B右子树BR的高度为H-1,那么插入新结点之后B的平衡因子为2,B变成了最小不平衡子树,但是我们这里设定的是A是最小不平衡子树来探讨的
- 所以这里假设所有的子树高度为H方便我们讨论不平衡的情况
RR
- 和上述描述的情况一样,如果AL、BL、BR这三部分的子树的高度不是H的话就无法满足在RR插入新结点之后A变成最小不平衡子树这个条件
LR
RL
查找效率分析
- 高度为h的平衡二叉树它的节点数最少的一种情况应该是一个根结点,加一个左子树高度为h-1并且我们要保证它的结点数最小,然后右子树可以让它高度为h-2并且也要保证它的结点数最少,所以就有了递推关系:nh = nh-1 + nh-2
平衡二叉树的删除
对比平衡二叉树的插入&删除
平衡二叉树的删除
- 是不是忘了二叉排序树是怎么删除结点的?哈哈哈看看下面的导图回忆下吧
AVL树删除操作——例1
- 此时没有找到最小的不平衡子树,说明刚刚的删除结点的操作并没有导致其任何一个祖先出现不平衡的现象,搞定
AVL树删除操作——例2
AVL树删除操作——例3
AVL树删除操作——例4
AVL树删除操作——例5
AVL树删除操作——例6
- 事实上我们选择哪一个结点作为其个子最高的孙子都是没问题的
红黑树RBT
为什么要发明红黑树
- 红黑树RBT和BST、AVL都是一个家族的树,都是用于查找的二叉树
- 既然AVL树的查找、插入、删除操作的时间复杂度已经在O(log2n)这个数量级,而且红黑树的查找、插入、删除操作也没有比AVL树更优秀,都是O(log2n)这个数量级,那为什么还要发明红黑树呢?
- 在AVL中每当我们删除或者插入一个新结点之后很有可能会破坏平衡二叉树“平衡”的特性,需要频繁调整树的形态
- 而红黑树RBT在插入/删除的很多时候都不会破坏“红黑”的特性,无需频繁调整树的形态,即便需要调整一般也都可以在常数级的时间内完成
红黑树大概会怎么考???
红黑树的定义
实例:一棵红黑树
练习:是否符合红黑树要求?
一种可能的出题思路
补充概念:结点的“黑高”
红黑树的性质
- 性质1可以和④和⑤推算而来,因为首先从根结点出发到达任意一个叶结点所经过的黑结点数量都是相同的,但是在这条路上又要求不红红,也就是说在根节点到叶结点的这条路上,不可能连续的出现两个红色的结点,这些红色的结点只有可能穿插在各个黑结点中间,并且根叶黑,首尾两端都是黑结点,假设每条路径都有n个黑结点,那么最少有0个红结点,最多只有n-1个红结点,因为路径最长的境况肯定是红节点把每一个黑结点之间的空隙都填满了
- 基于性质1我们也可以得出这样的结论:在红黑树当中,任何一个结点的左子树和右子树的高度之差不会超过两倍;这相比于AVL高度之差不超过1的特性来说不那么容易被破坏,所以红黑树的红黑特性不太容易被破坏,但是平衡二叉树的特性很容易被破坏,每一次的破坏都得进行平衡的调整,这也就是红黑树的插入和删除比平衡二叉树更高效的原因
红黑树的查找
- 和二叉排序树以及平衡二叉树的查找是相同的
红黑树的插入
回忆:AVL的插入
RBT的插入
- 20是根节点,我们要保证“根叶黑”,所以根节点统一都是黑色
- 从根节点出发,10比20更小,所以应该往左走,只要新插入的结点不是根节点就统一把它们涂红,这么做的原因是为了保证“黑路同”这个特性,如果新插入的结点是黑色就会导致左边黑路的长度比右边黑路的长度长,破坏了红黑树的特性
- 所以我们插入节点时会优先把它涂成红色,因为红节点不会增加黑路的长度
- 染色染的是刚刚发生调换处理的两个结点——红变黑黑变红
- 我们可以看到插入新结点的时候一定是破坏了红黑树”不红红“这个特性,因为其它三个特性不可能被这个新插入的红节点破坏
- 新结点不是根,所有的非根结点我们只需要关注其有没有引发”不红红“即可
红黑树练习方法——快乐小网站
与“黑高”相关的推论
红黑树的删除
B树
回忆:二叉查找树BST
- 二叉查找树无非就是用一个关键字把一个我们有可能搜索到我们目标关键字的这个范围(数域范围)分割成了两份
5叉查找树
5叉查找树如何查找
如何保证查找效率
- 为什么根节点可以搞特殊???
- 因为我们不能保证根节点有那么多个结点支撑它有那么多个分叉
B树
- 多路:任何一个结点可能会有多个分叉出现,我们可能会从多条路往下查找
- 平衡:对于每个节点来说它的任何一个子树的高度是相同的,即任何一个结点都是平衡的(绝对平衡)
- 不是终端结点的根节点至少要有2棵子树,是为了保证绝对平衡
- 叶子结点也就是失败结点,本质上就是一个null空指针,出现在同一层上是因为我们要求所有的结点都绝对平衡,因此所有的叶子结点一定都是同时出现在最下面这一层的
B树的高度
- n个关键字的B树必有n+1个叶子节点
- Why?因为B树当中的这n个关键字相当于是在(-∞,+∞)这个区间内插入了n个关键字,所以这n+1个关键字会把整个数值区间把它切分为n+1个部分,那这n+1个部分其实就对应了n+1种失败的情况,所有的失败情况都会体现在我们的叶子结点中,所以失败结点也肯定是n+1个
B树的插入和删除
B树的插入
- 注意这里我们每次新插入的元素一定是要插入最底层的结点当中的
- 如果90插在上面一层那么就意味着我们的失败结点就不是同一层了,不满足B树的特性
- 如果一个关键字,它因为分裂需要把它提到父结点当中,那么我们应该把这个关键字放到它所属的结点的这条指针所对应的这个点右边的位置
- 这么做可以保证我们这棵树依然保持B树的特性
B树的删除
- 相当于把非终端结点的删除操作转换成了对终端结点的删除操作
B+树
对比:分块查找
B+树
- 为什么非叶根结点至少要有两棵子树呢?
B+树的查找
- B+树无无论查找的成功与否,最终都一定要走到最下面的一层结点
- 因为在B+树中如果我们只是在分支结点中找到我们想要找到的那个关键字,我们的查找还是没有结束的,因为关键字代表的这个记录的实际信息到底存放在哪我们只有找到最下面一层叶子结点之后才可以找到这个记录对应的数据
- B+树除了从根节点开始逐层开始往下查找之外,我们也可以通过保存的指针p进行顺序查找
对比:B树的查找
- B+树无无论查找的成功与否,最终都一定要走到最下面的一层结点,因为上一层的分支节点信息一不能真实地反应出某一个关键字是否存在,二是我们只有找到最下面这一层的叶子结点之后才可以找到某一个关键字实际对应的这个记录它的存放位置
- 因为在B+树中如果我们只是在分支结点中找到我们想要找到的那个关键字,我们的查找还是没有结束的,因为关键字代表的这个记录的实际信息到底存放在哪我们只有找到最下面一层叶子结点之后才可以找到这个记录对应的数据
- 但是在B树的查找中我们有可能查找成功,停留在任何一层,因为如果查找到了我们就可以确定这个关键字是存在的,这要理解B树和B+树的本质区别(m叉排序树和多级分块查找)
B+树 v.s. B树
关键字和子树的对应情况
关键字数
关键字是否重复
结点
小拓展
- B+树里边这些一个一个的结点其实是存放在我们的磁盘里边的,也就是外存当中,操作系统对磁盘的读写一般是以磁盘块为单位的,所以一般来说B+树的一个节点就会对应存放在某一个磁盘块当中,所有的结点都是存放在不同的磁盘块里面的
- 因此我们对B+树的查找其实是这样的过程,我们要从根节点开始查找,系统在背后做的事情是会找到这个根节点它到底是存放在哪一个磁盘块当中,然后会把磁盘块读到内存当中,只有读到内存里计算机才可以处理查询对应的数据,读取完之后我们就可以确定我们要找的关键字应该要到哪一个分支上去查找,接下来系统会把对应的分块相关的数据再一次读入内存,然后计算机就可以开始查询这些分块里边的数据,我们可以知道再往下的一层叶子节点到底存放在磁盘当中的什么位置,所以接下来操作系统会把对应的这一块给读入内存,然后就可以找到欲查找的关键字所对应的记录应该是存放在磁盘当中的什么位置,所以接下来只需要在磁盘当中读出这个记录相关的数据信息就可以
- 对于B+树的查找,我们每查找一层结点的时候其实都需要进行一个读磁盘的操作,直到找到最下面一层的叶子节点,对于B树也是一样的,我们每查找一层结点的时候其实都需要进行一个读磁盘的操作,磁盘作为一种慢速设备,计算机读磁盘的操作其开销是很大的,因此就意味着我们的B+树高度越高,整个查找过程中读磁盘的次数也会更多,这会导致我们的查找速度更慢
- 怎么减少树的高度呢?每一个节点包含的关键字数量越多,就意味着这棵树总的高度就会越低,而且这些一个一个的结点其实是存放在我们的磁盘里边的,而每个磁盘块大小固定,所以我们就要让一个磁盘块中包含尽可能多的关键字的信息,这就是B+树这么设计的原因:B+树中,非叶子结点不含有该关键字对应记录的存储地址,这就意味着这一份记录可以花更少的空间来存储,可以使一个磁盘包含更多的关键字,使得B+树的阶更大,树高更矮,读磁盘次数更少,查找更快
散列查找Hash
散列表/哈希表Hash Table
- 每个数据元素的关键字和数据元素的实际存储地址之间有一个映射关系
处理冲突的方法——拉链法
散列查找
- 这里我们可以发现,分子的部分其实反应的是我们这个散列表存储了多少的记录/数据元素,而分母刚好等于散列表的长度
- 装填因子这个参数表示的是一个散列表它到底装得有多满,装填因子越大说明这个散列表装得越满,其大小会直接影响到散列表的查找效率
常见的散列函数
除留余数法
- 为什么p要取质数呢?因为取质数可以让不同关键字的冲突尽可能地少
直接定址法
数字分析法
平方取中法
- 相乘的时候中间的几位可能是和我们每一个数码位都是相关的
- 冲突太淦了,有没有一种方法能绝对地避免冲突呢?有!空间换时间!
处理冲突的方法——开放定址法
线性探测法
- 现在来探讨查找操作
- 拉链法由于是指针,指针域为null所以我们在查找失败的时候已经到了空指针就不用比较了,但是这里每个位置都是一个数,我们不确定这个数是否相等,所以仍需要一次比较
- 下面我们来探讨删除操作
- 现在来探讨查找效率
平方探测法
- 现在来探讨查找问题
- 非重点小坑:答案在《数论》
伪随机序列法
处理冲突的方法——再散列法