查找(平衡二叉树、红黑树、B树)

7.1 查找的基本概念

基本概念

查找表不是某一种特定的、指定的数据结构,他只是对于你要执行查找操作的数据结构的统称而已。
在这里插入图片描述

常见操作(查找、插入、删除)

在这里插入图片描述

查找算法的评价指标

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.2-1 顺序查找

在这里插入图片描述

顺序查找的实现

在这里插入图片描述

顺序查找的实现(数组0位置使用哨兵)

在这里插入图片描述
在这里插入图片描述

顺序查找的优化

将查找表的元素有序存放

在这里插入图片描述
在这里插入图片描述

将被查概率大的放前面

在这里插入图片描述

7.2-2 折半查找

折半查找适用于有序的顺序表(不是链表)

在这里插入图片描述

折半查找代码实现

在这里插入图片描述

查找效率分析

在这里插入图片描述

折半查找判定树的构造

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下图取mid的上限,上图取mid的下限。
在这里插入图片描述
平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

顺序查找不一定比折半查找慢

在这里插入图片描述

7.2-3 分块查找

分块查找的算法思想

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

用折半查找查索引

查找19

在这里插入图片描述
19<30, high=mid-1,mid=(low+high)/2
在这里插入图片描述
19>mid,low=mid+1
在这里插入图片描述
19<mid=20,high=mid-1,此时high<low
在这里插入图片描述
在这里插入图片描述
上面,在low>high前是low=high=mid,如果key(比如等于19)<mid(比如在这取20),那么low不移动,high往前移动一格。则如果我们查找key需要在low所指的分块中查找。

下面,在low>high前是low=high=mid,如果key(比如等于21)>mid(比如在这取20),那么low往后移动一格,high不移动。则如果我们查找key需要在low所指的分块中查找。

综上所述,若索引表中不包含目标关键字,则折半查找索引表最终停在low>high,要在low所指分块中查找。

查找21

在这里插入图片描述
21<30,high=mid-1,mid=low+high
在这里插入图片描述
21>mid,low=mid+1,mid=(low+high)/2,此时low=high=mid
在这里插入图片描述
21>mid,low=mid+1,如下图
在这里插入图片描述
若索引表中不包含目标关键字,则折半查找索引表最终停在low>high,要在low所指分块中查找。

查找效率分析

在这里插入图片描述
顺序查找查索引表
在这里插入图片描述
折半查找查索引表
在这里插入图片描述

若查找表是动态查找表(采用链式存储)(也利于添加删除元素)

在这里插入图片描述

7.3-1 二叉排序树

二叉排序树的定义

在这里插入图片描述

二叉排序树的查找

在这里插入图片描述
在这里插入图片描述

二叉排序树的插入

在这里插入图片描述
在这里插入图片描述

二叉排序树的构造

构建二叉排序树就是不断插入新节点的过程。
在这里插入图片描述
在这里插入图片描述

二叉排序树的删除

1、如果被删除结点是叶子节点

在这里插入图片描述

2、结点只有一棵左子树或右子树

在这里插入图片描述
在这里插入图片描述

3、若删除的结点既有左子树,又有右子树

用直接后继替代

在这里插入图片描述
在这里插入图片描述

用直接前驱替代

在这里插入图片描述
在这里插入图片描述

查找效率分析

在这里插入图片描述
在这里插入图片描述

7.3-2 平衡二叉树

平衡二叉树的定义

在这里插入图片描述
二叉排序树保持平衡可以保证二叉排序树的查找效率可以到达O(log2 n)数量级
在这里插入图片描述

在插入新结点后,如何保持平衡?( 调整最小不平衡子树)

在这里插入图片描述
在这里插入图片描述

调整最小不平衡子树

在这里插入图片描述

LL

这里我们假设在进行操作之后A结点是最小的不平衡子树。
只有要求AR、BL、BR都是高度H才能满足 进行操作之后A结点是最小的不平衡子树的假设。
如果AR、BL、BR中有其中某个或某些变换高度为H+1或H-1,则会出现可能不需要操作就是不平衡子树的情况或者操作之后还是平衡子树的情况或者操作之后B结点是最小的不平衡子树的情况。可自行试验验证,由此可知需要假定所有子树的高度都为H。
在这里插入图片描述

RR

在这里插入图片描述

LL和RR的代码思路

在这里插入图片描述

LR

在这里插入图片描述
在这里插入图片描述
下图不论是 加到BR(即C)的右子树了还是BR(即C)的左子树,进行的操作都是一样的,都是先进行左旋C再右旋C。(后续调整RL同理)
在这里插入图片描述

RL

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

调整最小不平衡子树 小结

在这里插入图片描述

填个坑

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
插入操作前是平衡树,插入后由于“最小不平衡子树”的高度变化了,导致了整个平衡树不平衡,经过调整后,“最小不平衡子树”变得平衡,该“最小不平衡子树”的高度经调整后恢复,也就恢复了整个树的平衡。
在这里插入图片描述

一些练习

练习1(RR型)

在这里插入图片描述
在这里插入图片描述

练习2(RL型)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

练习3(LR型)

在这里插入图片描述
在这里插入图片描述

查找效率分析

假设以nh表示深度为h的平衡树中含有的最少结点数。
深度h=0,没有结点,nh=0,
深度h=1,有一个结点,nh=1,
深度h=2,最少有两个结点,一个根节点,加一个左孩子或一个右孩子,nh=2,
深度为h的平衡树,最少有一个根节点 加 一个深度为h-1的子树 加 一个深度为h-2的子树,n(h-1)和n(h-2)分别表示深度为h-1的平衡树中含有的最少结点数 和 深度为h-2的平衡树中含有的最少结点数。这样n(h-1)+n(h-2)+1正好表示深度为h的平衡树中含有的最少结点数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.3-3 平衡二叉树的删除

平衡二叉树的删除 案例

在这里插入图片描述

1、删除结点

在这里插入图片描述

2.1没找到最小不平衡子树,完结撒花

从被删除节点那一路往上找(或者从调整后仍不平衡的子树那往上找),找到第一棵子树。
在这里插入图片描述

2.2 找到最小不平衡子树了

删除的是 55
在这里插入图片描述

3、在最小不平衡子树下,找个头最高的儿子孙子(如果个头最高的儿子或孙子有多个,那么任选一个即可)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、根据孙子的位置,调整平衡

在这里插入图片描述
在这里插入图片描述

5、如果不平衡向上传导,继续2

未出现不平衡向上传导,结束。
在这里插入图片描述

复习 二叉排序树如何删除结点

例1 用后继结点替代

删除结点75,
用后继结点77替代,由于77是叶子节点,可以直接删除。
在这里插入图片描述
在这里插入图片描述

例2 用前驱结点替代

删除结点75,用前驱结点60替代,因为被删除的结点60只有左子树,用子树替代删除位置即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

平衡二叉树删除操作时间复杂度

在这里插入图片描述

7.3-4 红黑树的定义和性质

为啥要发明红黑树?

在这里插入图片描述

红黑树的定义

红黑树是二叉排序树,满足左小于等于根小于等于右的特性。
不存在两个相邻的红结点,但是允许有两个相邻的黑结点。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

红黑树的性质

性质1:从根节点到叶结点的最长路径不大于最短路径的2倍。可由④,⑤推出
④不存在两个相邻的红结点(即红结点的父节点和孩子结点均是黑色)
⑤对每个结点,从该节点到任一叶结点的简单路径上,所含黑结点的数目相同。

对于每个结点,从根节点出发到任一叶结点所经过的黑节点数量都是相同的,但是在这条路上,要求不能存在两个相邻的红结点。红色的结点只有可能穿插在黑结点中。路径最长的情况肯定就是红结点穿插在每一个黑结点的中间,路径最短的情况就是只有黑结点。
在这里插入图片描述

红黑树的查找

在这里插入图片描述

7.3-5 红黑树的插入

在这里插入图片描述
该红结点在他爷结点的左孩子的左边,所以是LL.
在这里插入图片描述
在这里插入图片描述
左根右,根叶黑,黑路同,不红红 四个原则中,左根右,根叶黑是不会被破坏的,由于插入非根节点都先涂成红色,该红结点的插入并不会导致他上方的任何一个结点的黑路变长,所以黑路同 这一原则也不会被违背,只有不红红可能被违背。所以我们插入结点后只须判断不红红就好了。
在这里插入图片描述
将爷结点视为新节点,如果红黑树没有被继续破坏,那么被视为新节点的这个爷结点不需要发生变化。
下图由于爷结点30不是根节点,所以不用必须是黑色,该爷结点呈红色并没有破坏红黑树,所以呈红色即可,也保证了黑路同 这一原则。
(这里不是特清楚,应该是将该爷结点视为新结点,又因为该爷结点不是根节点,所以是红色)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上图左、右双旋转指的是:先结点23左旋换到父节点22这个位置,在右旋换到爷结点25这个位置。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下图是将叔父爷染色后,出现了新的“不红红”
在这里插入图片描述
下图儿结点23,父节点20,爷结点30
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

在这里插入图片描述

与黑高相关的推论

在这里插入图片描述
在这里插入图片描述

7.3-6 红黑树的删除

红黑树删除比红黑树插入难的多,
在这里插入图片描述
在这先不展开讲解 红黑树的删除操作了。仅先知道以上几点即可。
红黑树22年加入408考纲。

7.4-1 B树

B树的引入

在这里插入图片描述
在这里插入图片描述
查找目标9,先对比22,小于22沿着22左边的分叉下去,和5进行比较,大于5,和11比较,小于11,从5和11中间的分叉下去,和6比较,大于6,和8比较,大于8,和9比较,等于9,查找成功。
在这里插入图片描述
查找目标9,先对比22,大于22沿着22右边的分叉下去,和36进行比较,大于36,和45比较,小于45,从36和45中间的分叉下去,和40比较,大于40,和42比较,小于42,从40和42中间的分叉下去,是null是空,查找失败。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

B树的定义(B树可以降低树的高度,提高查询效率)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

B树的特性

在这里插入图片描述

B树的最小高度、最大高度

最小高度

在这里插入图片描述

最大高度(第一种求法)

下图:n个关键字的B树必有n+1个叶子结点,n个关键字相当于在(-∞,∞)中插入了n个关键字,n个关键字会把整个数值区间分割为n+1个部分,这n+1部分就相当于n+1种失败的情况,所有这些有可能出现的失败情况都会体现在叶子结点(失败结点)中,所以失败结点(叶子节点)的个数也是n+1个。

下图在这里插入图片描述这个为何大于等于我暂时不能理解,
最大高度为h的m阶B树的第h+1层共有叶子节点有在这里插入图片描述,n个关键字的B树必有n+1个叶子结点,那么即可认为在这里插入图片描述
得到在这里插入图片描述,因为最大高度才可能是这个在这里插入图片描述,那么实际高度h是小于这个的,从而得出了在这里插入图片描述
在这里插入图片描述

最大高度(第二种求法)

在这里插入图片描述

B树的高度的范围

在这里插入图片描述

总结

在这里插入图片描述

7.4-2 B树的插入删除

B树的插入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

B树的删除(以下为一个整体、一个例子)

直接删除是终端节点

在这里插入图片描述
在这里插入图片描述

删除非终端结点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对非终端结点关键字的删除,必然可以转化为对终端结点的删除操作。所以下面我们主要讨论对终端结点的删除。

删除终端结点并低于下限(兄弟够借)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

删除终端结点并低于下限(兄弟不够借)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

在这里插入图片描述

7.4-3 B+树

B+树概念

在这里插入图片描述
对于第2条,非叶根节点,以下。
在这里插入图片描述

B+树中的查找

多路查找

查找成功

从根节点出发,9小于15,15表示他所指向的一整个分块当中最大的是15,沿15这个分叉下去寻找,和3比较,大于3,指针后移,和9比较,等于9。在B+树中,如果只是在分支结点中,找到我们想要找的关键字,那我们的查找并没有结束,因为9号的实际信息存放在哪,我们只有找到最下层的叶子结点之后才可以找到这个数据,这个信息。所以指针继续往下移,然后从左往右依次检查,对比6,8,9,找到9,通过9这一项保存的指针信息,就能找到9号结点的详细信息。
在这里插入图片描述

查找失败

查找7,先和15比较,小于15,沿着指针去下面找,和3比较,7大于3,和9比较,7小于9,沿着指针去下面找,比较6,7大于6,比较8,7小于8,由于此时已经是最下层结点了,所以还没找到7,那么7就是在这里面不存在的。查找失败。
在这里插入图片描述

对比B树查找

在这里插入图片描述

顺序查找

直接从p指针那开始比较,和1、3、6、8、9分别比较,结点9等于9,在9的位置停下,向下查询记录,获取该结点详细信息。
在这里插入图片描述

B+树vsB树

区别1

在这里插入图片描述
在这里插入图片描述

区别2

在这里插入图片描述
在这里插入图片描述

区别3

在这里插入图片描述
在这里插入图片描述

区别4

在这里插入图片描述
在这里插入图片描述

区别5(有关操作系统,之后再自行查询深入理解)

下图中“B+树的阶”中的阶就是5阶、6阶,每个结点最多有几个关键字的意思。
在这里插入图片描述

总结

下图谈到B+树类比于分块查找,我感觉是在分块中,比如在[20,30]这块中查找30,那么比对30知道在这块后,在这块里面还能找到30。
在这里插入图片描述

7.5-1 散列查找(上)

哈希表定义

在这里插入图片描述

处理冲突的方法(拉链法)

拉链法示例

在这里插入图片描述

查找元素举例(查找成功)

在这里插入图片描述

查找元素举例(查找失败)

在这里插入图片描述

平均查找长度的计算(在拉链法下)

查找成功的平均长度

在这里插入图片描述

查找失败的平均长度

查找失败,假定查找失败的关键字映射到任何一个地址的概率都是相同的,总共可能映射到0到12共13个地址,每种可能是1/13.
装填因子越多,散列表装的越满。
在这里插入图片描述

常见的几种散列函数

除留余数法

在这里插入图片描述
在这里插入图片描述

直接定址法

在这里插入图片描述

数字分析法

在这里插入图片描述

平方取中法

解释一下均小于散列地址所需的位数,比如要存储关键字1000,2000,3000,但是散列地址是从10000到30000,那么关键字1000,2000,3000均是4位,均小于散列地址10000到30000的5位,那么就需要平方来扩大位数。
在这里插入图片描述
而中间绿色方框的数字和1200这个关键字的每位都相关。
在这里插入图片描述

7.5-2 散列查找(下)

处理冲突的方法(开放定址法)

线性探测法

插入1时发生冲突,经过计算后插入2位置。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下图:H(key)=27%13=1,H0=(1+0)%16=1,H1=(1+1)%16=2,H2=(1+2)%16=3,H3=(1+3)%16=4,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
哈希函数 H(key)=key%13值域 [0,12]
冲突处理函数 H=(H(key) + d)%m值域 [0,15]

查找操作

查找成功
在这里插入图片描述
查找失败
在这里插入图片描述
遇到空位置
在这里插入图片描述
查找中遇到空位置(不过这个空位置是删除结点后留出的空位置)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

查找效率分析

查找成功ASL
在这里插入图片描述
查找失败ASL
在这里插入图片描述
在这里插入图片描述

平方探测法

在这里插入图片描述
在这里插入图片描述
查找元素
在这里插入图片描述
非重点小坑:散列表长度m必须是一个可以表示成4j+3的素数,才能探测到所有位置
在这里插入图片描述

伪随机序列法

在这里插入图片描述

处理冲突的方法(再散列法)

在这里插入图片描述

散列查找总结

在这里插入图片描述

下面是一个简单的 Java 多路查找(Trie)的代码示例: ```java public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } public void insert(String word) { TrieNode node = root; for (char c : word.toCharArray()) { if (node.children[c - 'a'] == null) { node.children[c - 'a'] = new TrieNode(); } node = node.children[c - 'a']; } node.isEndOfWord = true; } public boolean search(String word) { TrieNode node = root; for (char c : word.toCharArray()) { if (node.children[c - 'a'] == null) { return false; } node = node.children[c - 'a']; } return node.isEndOfWord; } public boolean startsWith(String prefix) { TrieNode node = root; for (char c : prefix.toCharArray()) { if (node.children[c - 'a'] == null) { return false; } node = node.children[c - 'a']; } return true; } private class TrieNode { private TrieNode[] children; private boolean isEndOfWord; public TrieNode() { children = new TrieNode[26]; } } } ``` 在这个示例中,我们定义了一个 Trie 类,其中包含一个内部 TrieNode 类,用于表示 Trie 中的节点。TrieNode 类有一个布尔型变量 isEndOfWord,用于指示该节点是否是一个单词的结尾。Trie 类有三个方法:insert、search 和 startsWith。insert 方法用于将单词插入到 Trie 中,search 方法用于查找单词是否在 Trie 中,startsWith 方法用于查找是否存在以给定前缀开头的单词。 在 insert、search 和 startsWith 方法中,我们首先将指针 node 初始化为根节点。然后,对于单词中的每个字符,我们检查该字符的子节点是否存在。如果子节点不存在,则创建一个新的 TrieNode,并将其设置为该字符的子节点。最后,我们将指针 node 移动到该字符的子节点。在查找单词时,如果我们到达一个节点,其 isEndOfWord 变量为 true,则说明该单词在 Trie 中,如果到达的节点没有对应的子节点,则该单词不在 Trie 中。在查找以给定前缀开头的单词时,我们只需要检查 Trie 中是否存在以该前缀开头的单词即可。 注意,在这个示例中,我们假设输入的单词只包含小写字母。如果输入包含其他字符,我们需要对代码进行适当的修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值