1. 二叉排序树BST
1.1 二叉排序树的定义
二叉排序树,又称二叉查找树,二叉检索树(BST,Binary Search Tree)
一棵二叉树或者是空二叉树,或者是具有如下性质的二叉树:
- 左子树上所有结点的关键字均小于根结点的关键字;
- 右子树上所有结点的关键字均大于根结点的关键字。
- 左子树和右子树又各是一棵二叉排序树。
左子树结点值 < 根结点值 < 右子树结点值 , 进行中序遍历,可以得到一个递增的有序序列
适用范围: 二叉排序树可用于元素的有序组织、搜索
1.2 二叉排序树的查找
1.2.1 算法流程
若树非空,目标值与根结点的值比较;
4. 若相等,则查找成功;
5. 若小于根结点,则在左子树上查找;
6. 否则在右子树上查找;
查找成功,返回结点指针;查找失败返回NULL 。
1.2.2 代码实现
typedef struct BSTNode{
int key;
struct BSTNode *lchild, *rchild;
}BSTNode, *BSTree;
//在二叉排序树中查找值为key的结点(非递归)
//最坏空间复杂度:O(1)
BSTNode *BST_Search(BSTree T, int key){
while(T!=NULL && key!=T->key){ //若树空或等于跟结点值,则结束循环
if(key<T->key) //值小于根结点值,在左子树上查找
T = T->lchild;
else //值大于根结点值,在右子树上查找
T = T->rchild;
}
return T;
}
//在二叉排序树中查找值为key的结点(递归)
//最坏空间复杂度:O(h)
BSTNode *BSTSearch(BSTree T, int key){
if(T == NULL)
return NULL;
if(Kry == T->key)
return T;
else if(key < T->key)
return BSTSearch(T->lchild, key);
else
return BSTSearch(T->rchild, key);
}
1.3 二叉排序树的插入操作
1.3.1 算法流程
- 若原二叉排序树为空,则直接插入结点;否则;
- 若关键字k小于根结点值,则递归插入到左子树;
- 若关键字k大于根结点值,则递归插入到右子树。
最坏空间复杂度:O(h)
1.3.2 代码实现
//在二叉排序树中插入关键字为k的新结点(递归)
//最坏空间复杂度:O(h)
int BST_Insert(BSTree &T, int k){
if(T==NULL){ //原树为空,新插入的结点为根结点
T = (BSTree)malloc(sizeof(BSTNode));
T->key = k;
T->lchild = T->rchild = NULL;
return 1; //插入成功
}
else if(K == T->key) //树中存在相同关键字的结点,插入失败
return 0;
else if(k < T->key)
return BST_Insert(T->lchild,k);
else
return BST_Insert(T->rchild,k);
}
1.4 二叉排序树的构造
依次将每个关键字插入到二叉排序树中(不同的关键字序列可能得到同款二叉排序树,也可能得到不同款二叉排序树)
//按照str[]中的关键字序列建立二叉排序树
void Crear_BST(BSTree &T, int str[], int n){
T = NULL; //初始时T为空树
int i=0;
while(i<n){
BST_Insert(T,str[i]); //依次将每个关键字插入到二叉排序树中
i++;
}
}
1.5 二叉排序树的删除
1.5.1 算法流程
先搜索找到目标结点:
- 若被删除结点z是叶结点则直接删除,不会破坏二叉排序树的性质;
- 若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置;
- 若结点z有左、右两棵子树,则令z的直接后继 (或直接前驱) 替代z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一或第二种情况。
- z的后继:z的右子树中最左下结点(该节点一定没有左子树)=347x240)
- z的前驱:z的左子树中最右下结点(该节点一定没有右子树)
1.6 查找效率分析
查找长度: 在查找运算中,需要对比关键字的次数称为查找长度,反映了查找操作时间复杂度
- 若树高h,找到最下层的一个结点需要对比 h 次.
- 最好情况:n个结点的二叉树最小高度为 ⌊ log 2 n ⌋ + 1 \left \lfloor \log_{2}n \right \rfloor+1 ⌊log2n⌋+1。平均查找长度= O( log 2 n ) \log_{2}n) log2n)。
- 最坏情况:每个结点只有一个分支,树高h=结点数n。平均查找长度=O(n)。
1.6.1 查找成功的情况下
1.6.1 查找失败的情况下(需补充n+1个失败结点)
2. 平衡二叉树
2.1 基础概念
2.1.1 定义
平衡二叉树(Balanced Binary Tree),简称平衡树(AVL树,G. M. Adelson-Velsky和E. M. Landis))——树上**任一结点的左子树和右子树的高度之差不超过1。**又称自平衡二叉查找树。平衡二叉树必定是二叉搜索树,反之则不一定。
结点的平衡因子=左子树高-右子树高。
- 平衡二叉树结点的平衡因子的值只可能是−1、0或1
- 只要有任一结点的平衡因子绝对值大于1,就不是平衡二叉树
- 左结点小于根节点,右结点大于根节点(或者左大于中大于右)
2.1.2 结点代码实现
//平衡二叉树结点
typedef struct AVLNode{
int key; //数据域
int balance; //平衡因子
struct AVLNode *lchild; *rchild;
}AVLNode, *AVLTree;
2.2 平衡二叉树的插入
2.2.1 过程分析
在插入操作中,只要将最小不平衡子树调整平衡,则其他祖先结点都会恢复平衡。
2.2.2 调整最小不平衡子树(四种情况)
- 调整最小不平衡子树(LL)
LL平衡旋转(右单旋转)。由于在结点A的左孩子(L)的左子树(L)上插入了新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要一次向右的旋转操作。将A的左孩子B向右上旋转代替A成为根结点,将A结点向右下旋转成为B的右子树的根结点,而B的原右子树则作为A结点的左子树。
- 调整最小不平衡子树(RR)
RR平衡旋转(左单旋转)。由于在结点A的右孩子(R)的右子树(R)上插入了新结点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要一次向左的旋转操作。将A的右孩子B向左上旋转代替A成为根结点,将A结点向左下旋转成为B的左子树的根结点,而B的原左子树则作为A结点的右子树.
代码实现
- 调整最小不平衡子树(LR):由于在 A 的左孩子(L)的右子树(R)上插入新结点,A 的平衡因子由 1 增至 2,导致以 A 为根的子树失去平衡,需要进行两次旋转操作,先左旋转后右旋转。先将 A 结点的左孩子 B 的右子树的根结点 C 向左上旋转提升到 B 结点的位置,然后再把该 C 结点向右上旋转提升到 A 结点的位置。
也有可能是插入到C的右子树
- 调整最小不平衡子树(RL)
由于在 A 的右孩子(R)的左子树(L)上插入新结点,A 的平衡因子由 -1 减至 -2,导致以 A 为根的子树失去平衡,需要进行两次旋转操作,先右旋转后左旋转。先将A 结点的右孩子 B的左子树的根结点 C 向右上旋转提升到 B 结点的位置,然后再把该 C 结点向左上旋转提升到 A 结点的位置。
插入操作导致“最小不平衡子树”高度+1,经过调整后高度恢复,在插入操作中,只要将
最小不平衡子树调整平衡,则其他祖先结点都会恢复平衡
2.2.3 实战练习
- RR型
- RL型
3. LR型
2.3 查找效率分析
若树高为h,则最坏情况下,查找一个关键字最多需要对比 h 次,即查找操作的时间复杂度不可能
超过 O(h)
平衡二叉树——树上任一结点的左子树和右子树的高度之差不超过1
n3=4,n4=7,n5=12,n6=20
2.4 平衡二叉树的删除
2.4.1 平衡二叉树的删除操作要求:
• 删除结点后,要保持二叉排序树的特性不变(左<中<右)
• 若删除结点导致不平衡,则需要调整平衡
2.4.2 平衡二叉树的删除操作具体步骤:
①删除结点(方法同“二叉排序树”)
②一路向北找到最小不平衡子树,找不到就完结撒花
③找最小不平衡子树下,“个头”最高的儿子、孙子
④根据孙子的位置,调整平衡(LL/RR/LR/RL)
孙子在LL:儿子右单旋
• 孙子在RR:儿子左单旋
• 孙子在LR:孙子先左旋,再右旋
• 孙子在RL:孙子先右旋,再左旋
⑤如果不平衡向上传导,继续②
对最小不平衡子树的旋转可能导致树变矮,从而导致上层祖先不平衡(不平衡向上传递)
例1:
- 删除
- 找最小不平衡子树
- 找最小不平衡子树下,“个头”最高的儿子、孙子
根据孙子的位置,调整平衡(LL/RR/LR/RL)
孙子在RR:儿子左单旋
例2:
1.删除结点
2.一路向北找到最小不平衡子树,找不到就完结撒花
3.找最小不平衡子树下,“个头”最高的儿子、孙子
4.根据孙子的位置,调整平衡(LL/RR/LR/RL)
孙子在RR:儿子左单旋
5.如果不平衡向上传导,继续②
2.4.3 删除效率
平衡二叉树删除操作时间复杂度=O(
l
o
g
2
n
log_{2}n
log2n)
A,根结点度可以为1,所以错
B,最小元素可以是根节点,有一个左孩子
C,不一定是叶子结点,因为如果插入后不平衡了,可能需要旋转
D,降序序列说明左大于中大于右,最大的元素一定是最左的
先按BST插入,也就是按照大小从上到下比较,将关键字插入到叶结点,如果插入后不再平衡,需要旋转一下
3. 红黑树(Red-Black Tree)RBT
3.1 为什么要发明 红黑树?
3.11 效率对比
- 如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。
- 平衡二叉树 AVL:插入/删除 很容易破坏“平衡”特性,需要频繁调整树的形态。如:插入操作导致不平衡,则需要先计算平衡因子,找到最小不平衡子树(时间开销大),再进行 LL/RR/LR/RL 调整
- 红黑树 RBT:插入/删除 很多时候不会破坏“红黑”特性,无需频繁调整树的形态。即便需要调整,一般都可以在常数级时间内完成
3.1.2 红黑树与AVL的区别
红黑树(Red-Black Tree)和AVL树都是自平衡的二叉搜索树,它们在保持树的平衡性和提供高效的插入、删除和查找操作方面有相似的目标。然而,它们在实现上有一些区别:
平衡性要求:AVL树要求左右子树的高度差不超过1,而红黑树没有明确的高度差限制,它只要求满足红黑树的五个性质即可。
平衡调整操作:AVL树在插入或删除节点后,可能需要进行更多的旋转操作来保持平衡,以满足高度差限制。而红黑树的平衡调整操作相对较少,通常只需要进行颜色变换和旋转。
维护开销:由于AVL树要求更严格的平衡,它的平衡调整操作相对较多,因此在频繁的插入和删除操作时,AVL树可能需要更多的平衡调整,导致维护开销较大。而红黑树的平衡调整操作相对较少,所以在插入和删除操作频繁的情况下,红黑树通常具有更好的性能。
内存占用:AVL树通常需要额外的平衡因子来记录每个节点的高度差,因此占用的内存空间相对较大。而红黑树只需要一个额外的颜色属性来记录节点的颜色,因此在内存占用上相对更优。
3.1.3 适用范围
- 平衡二叉树:适用于以查为主、很少插入/删除的场景
- 红黑树:适用于频繁插入、删除的场景,实用性更强
3.2 考试考法
- 红黑树的定义、性质——选择题
- 红黑树的插入/删除——要能手绘插入过程(不太可能考代码,略复杂),删除操作也比较麻烦,也许不考
3.3 红黑树的定义
3.3.1 性质
- 必须是二叉排序树左子树结点值 ≤ 根结点值 ≤ 右子树结点值,且每个结点或是红色,或是黑色的——左根右;
- 根节点是黑色的,叶结点 (外部结点、NULL结点、失败结点) 均是黑色的——根叶黑;
- 不存在两个相邻的红结点(即红结点的父节点和孩子结点均是黑色)——不红红;
- 对每个结点,从该节点到任一叶结点(为有向图路径只能向下)的简单路径上,所含黑结点的数目相同——黑路同。
- 结点的黑高bh --从某结点出发 (不含该结点)到达任一空叶结点的路径上黑结点总数。
性质1:从根节点到叶结点的最长路径不大于最短路径的2倍。 - **性质1证明:**任何一条查找失败路径上黑结点数量都相同,而路径上不能连续出现两个红结点,即红结点只能穿插在各个黑结点中间
性质2:有n个内部节点的红黑树高度 h ⩽ 2 log 2 ( n + 1 ) h\leqslant 2\log_{2}(n+1) h⩽2log2(n+1)
与“黑高”相关的推论
- 结点的黑高bh --从某结点出发 (不含该结点)到达任一空叶结点的路径上黑结点总数。
1.根节点黑高为 h 的红黑树,内部结点数(关键字)至少有多少个?
回答:内部结点数最少的情况——总共h层黑结点的满树形态
结论:若根节点黑高为
h
h
h,内部结点数(关键字)最少有
2
h
−
1
2^{h}-1
2h−1个
性质2证明: 若红黑树总高度=h,则根节点黑高 ≥ h / 2 h/2 h/2,因此内部结点数 n ≥ 2 h / 2 − 1 n ≥ 2^{h/2}-1 n≥2h/2−1,由此推出 h ≤ 2 log 2 ( n + 1 ) h ≤ 2\log_{2}(n+1) h≤2log2(n+1)
2.根节点黑高为
h
h
h 的红黑树,内部结点数(关键字)至多有多少个?
回答:内部结点数最多的情况——h层黑结点,每一层黑结点下面都铺满一层红结点。共
2
h
2h
2h层的满树形态
结论:若根节点黑高为h,内部结点数(关键字)最多有
2
2
h
−
1
2^{2h}-1
22h−1 个
3.3.2 实例:
下面这些选项中,满足“红黑树”定义的是(D )
A:不红红 B:跟叶黑 C:黑路同
3.3.3 结点定义
3.4 红黑树的查找
与 BST、AVL 相同,从根出发,左小右大,若查找到一个空叶节点,则查找失败
3.4.1 查找效率
红黑树查找操作时间复杂度 = O(
l
o
g
2
n
log_{2}n
log2n),查找效率与AVL树同等数量级
3.4.2 平均查找长度 ASL
查找成功:(1+22+34+43)/ 10 =2.9
查找失败时 (45 + 5*6) / 11 = 4.55
3.5 红黑树的插入
3.5.1 插入流程
- 先查找,确定插入位置(原理同二叉排序树),插入新结点
- 新结点是根-一染为黑色
- 新结点非根一一染为红色
- 若插入新结点的父结点是黑色,无需做任何操作,依然满足红黑树定义,插入结束
- 若插入新结点的父结点是红色,需要调整, ,如何调整:此时爷爷必存在且为黑色,看新结点叔叔的脸色
- 黑叔:旋转+染色
- LL型:右单旋,父换爷+染色原父和爷换色(父黑爷红)
- LR型:左旋变成LL、右旋,儿换爷+染色相当于原结点和原爷换色
- RR型:左单旋,父换爷+原父和爷换色(父黑爷红)
- RL型:右、左双旋,儿换爷+染色
- 红叔:染色+变新
- 叔父爷染色,父节点和叔节点都变黑,祖父变红,祖父变成当前节点爷变为新结点
- 黑叔:旋转+染色
注意:插入非根节点应该是红色。
原因:根据红黑树特性之一【任意一节点到每个叶子节点的路径都包含数量相同的黑节点】,红色在父节点(如果存在)是黑色节点时,红黑树的黑色平衡不会被破坏,所以不需要进行自平衡操作。但如果插入节点是黑色,那么插入位置所在的子树黑色节点总是多1,必须做自平衡操作。
空节点也算黑叔
3.5.2插入实例
1.插入根节点20,插入10,5,黑叔LL
2.插入30红叔(叔父爷,爷变新)
3.插入40黑叔RR
4.插入57红叔
5.插入新结点3的父结点是黑色,无需做任何操作
6.插入2黑叔LL
7.插入4红叔
8. 9.10插入新结点35,25,18的父结点是黑色,无需做任何操作
11.插入22,红叔(叔父爷·,爷变新)
12.插入23黑叔,LR
左旋变成LL
右旋
儿换爷,儿黑爷红
14.插入24红叔
叔父爷染色,爷变新
新结点(原爷)黑叔LR
左旋
右旋
儿换爷,23黑30红
3.6 红黑树的删除操作
重要考点:
①红黑树删除操作的时间复杂度 = O(log2n)
②在红黑树中删除结点的处理方式和“二叉排序树的删除”一样
③按②删除结点后,可能破坏“红黑树特性”,此时需要调整结点颜色、位置,使其再次满
足“红黑树特性”。
4. 2-3 树
要动态保证 AVL 的平衡需要很多操作,这些操作会影响整个数据结构的性能,除非是在树的结构变化特别少的情形下,否则 AVL 树平衡带来的搜索性能提升有可能还不足为了平衡树所带来的性能损耗。
因此,引入了 2-3 树来提升效率。2-3 树本质也是一种平衡搜索树,但 2-3 树已经不是一棵二叉树了,因为 2-3 树允许存在 3 这种节点,3- 节点中可以存放两个元素,并且可以有三个子节点。
4.1 定义
(1)2-3 树要么为空要么具有以下性质:
(2)对于 2- 节点,和普通的 BST 节点一样,有一个数据域和两个子节点指针,两个子节点要么为空,要么也是一个2-3树,当前节点的数据的值要大于左子树中所有节点的数据,要小于右子树中所有节点的数据。
(3)对于 3- 节点,有两个数据域 a 和 b 和三个子节点指针,左子树中所有的节点数据要小于a,中子树中所有节点数据要大于 a 而小于 b ,右子树中所有节点数据要大于 b 。
4.2 2-3 树性质
(1)对于每一个结点有 1 或者 2 个关键码。
(2)当节点有一个关键码的时,节点有 2 个子树。
(3)当节点有 2 个关键码时,节点有 3 个子树。
(4)所有叶子点都在树的同一层。
4.3 2-3树查找
2-3 树的查找类似二叉搜索树的查找过程,根据键值的比较来决定查找的方向。
例如在下图 所示的 2-3 树中查找节点
4.3.1 查找成功
4.3.2 查找失败
2-3 树作为一种平衡查找树,查询效率比普通的二叉排序树要稳定许多。但是2-3树需要维护两种不同类型的结点,查找和插入操作的实现需要大量的代码,而且它们所产生的额外开销可能会使算法比标准的二叉查找树更慢。
5. 2-3-4树
5.1 定义
2-3-4树是扩展于二叉树的多叉树,二叉树是一个数据项,两个子节点,234树是最少一个数据项,最多3个数据项,最少2个子节点,最多4个子节点,也就是说2-3-4树当只有一个数据项时非叶子节点有且只有2个子节点,2个数据项时3个子节点,3个数据项时4个子节点,子节点数量可以为2,3,4,所以叫2-3-4树。
运算限制:它是平衡树,有序树,添加操作只发生在叶子节点上
4.2 特性
(1)每个节点每个节点有 1、2 或 3 个 key ,分别称为 2- 节点,3- 节点,4- 节点。
(2)所有叶子节点到根节点的长度一致(也就是说叶子节点都在同一层)。
(3)每个节点的 key 从左到右保持了从小到大的顺序,两个 key 之间的子树中所有的 key 一定大于它的父节点的左 key ,小于父节点的右 key 。
非叶节点数据项和其子节点数量的关系:
- 有一个数据项的节点总是有两个子节点
- 有两个数据项的节点总是有三个子节点
- 有三个数据项的节点总是有四个子节点
即非叶节点的子节点数总是比它含有的数据项多1
2.2-3-4树数据项之间的大小关系
- 根是child0的子树的所有子节点的关键字值小于key0;
- 根是child1的子树的所有子节点的关键字值大于key0并且小于key1;
- 根是child2的子树的所有子节点的关键字值大于key1并且小于key2;
- 根是child3的子树的所有子节点的关键字值大于key2。
由于2-3-4树中一般不允许出现重复关键值,所以不用考虑比较关键值相同的情况
3.与红黑树的联系
红黑树就是由234树演变出来的。(红黑树是234树的一种实现),了解了234树才能明白红黑树旋转和颜色变化的底层逻辑。
结点类型
红黑树对应关系图示
上图中没有画出具体的子结点 仅用虚线框标识了一下子结点的取值范围。
不难看出,只要将红黑树中的红结点向上一提,与父结点合并,就反推出了234树。
4.3 搜索(查找)2-3-4树
2-3-4 树的查找类似了二叉树的查找过程,通过键值的比较来决定遍历方向。从根节点开始搜索,(除非查找的关键 字值就是根,)选择关键字值所在的合适范围,转向那个方向,直到找到为止。
4.3.1 查找成功
4.3.1 查找失败
4.3.2 插入
如果 2-3-4 树中已存在当前插入的 key ,则插入失败,否则最终一定是在叶子节点中进行插入操作,因为查找过程的结束位置在叶子节点。如果插入到有子节点的节点里,那么子节点的数量就要发生变化来维持树的结构,因为在2-3-4树中节点的子节点要比数据项多1。
-
非 4- 节点插入
如果待插入的节点不是 4- 节点,那么直接在该节点插入。 -
4- 节点插入
如果待插入的节点是个 4- 节点,那么应该先分裂该节点然后再插入。一个 4- 节点可以分裂成一个根节点和两个子节点(这三个节点各含一个 key )然后在子节点中插入,我们把分裂形成的根节点中的 key 看成向上层插入的 key
4.4 删除
执行删除之前需要对删除 key 进行查找,若查找失败则无法删除。查找成功,才可删除 key 。删除节点情况有以下几种:
4.4.1删除的节点不为 2- 节点
删除的节点不为 2- 节点,则将要删除的目标 key 直接删除即可。
其他过于复杂省略
5. B树
5.1 B树定义
B树,⼜称多路平衡查找树,B树中所有结点的孩⼦个数的最⼤值称为B树的阶,通常⽤m表示。⼀棵m阶B树或为空树,或为满⾜如下特性的m叉树:
- 树中每个结点⾄多有 m 棵⼦树,即⾄多含有 m-1 个关键字。
- 若根结点不是终端结点,则⾄少有两棵⼦树。
- 除根结点外的所有⾮叶结点⾄少有 ⌈ m / 2 ⌉棵⼦树,即⾄少含有 ⌈ m / 2 ⌉ - 1个关键字。(为了保证查找效率,每个结点的关键字不能太少)
- 所有的叶结点都出现在同⼀层次上,并且不带信息(可以视为外部结点或类似于折半查找判定树的查找失败结点,实际上这些结点不存在,指向这些结点的指针为空)。
5.2m阶B树的核⼼特性:
- 根节点的⼦树数∈ [ 2 , m ] ,关键字数 ∈[1,m−1]。
- 其他结点的⼦树数∈ [ ⌈ m / 2 ⌉ , m ] ;关键字数∈ [ ⌈ m / 2 ⌉− 1 , m − 1 ]。
- 对任⼀结点,其所有⼦树⾼度都相同。
- 关键字的值:⼦树0 < 关键字1 < ⼦树1 < 关键字2 < ⼦树2 <…. (类⽐⼆叉查找树左<中<右),结点内关键
字有序
B树的⾼度:
含 n 个关键字的 m叉B树,最⼩⾼度、最⼤⾼度是多少?(算B树的⾼度不包括叶⼦结点(失败结点))
l o g m ( n + 1 ) ≤ h ≤ l o g ⌈ m / 2 ⌉ n + 1 2 + 1 log_{m}(n+1)≤h≤log_{⌈m/2⌉}\frac{n+1}{2}+1 logm(n+1)≤h≤log⌈m/2⌉2n+1+1
5.3 查找
5.3.1 查找成功
5.3.2 查找失败
5.4 B树的插⼊
5阶B树——结点关键字个数 ≤n≤m-1 即:2≤n≤4 (注:此处省略失败结点)
在插⼊key后,若导致原结点关键字数超过上限,则从中间位置(⌈ m / 2 ⌉ )将其中的关键字分为两部分,左部分包
含的关键字放在原结点中,右部分包含的关键字放到新结点中,中间位置( ⌈ m / 2 ⌉)的结点插⼊原结点的⽗结点
新元素⼀定是插⼊到最底层“终端节点”,⽤“查找”来确定插⼊位置
思考:80要放到⽗节点中,放在哪个位置合适
若其⽗结点的关键字个数也超过了上限,则继续进⾏这种分裂操作,直⾄这个过程传到根结点为⽌,进
⽽导致B树⾼度增1。
5.5 B树的删除
-
若被删除关键字在终端节点,则直接删除该关键字(要注意节点关键字个数是否低于下限 ⌈m/2⌉ − 1)
-
若被删除关键字在⾮终端节点,则⽤直接前驱或直接后继来替代被删除的关键字
-
直接前驱:当前关键字左侧指针所指⼦树中“最右下”的元素
-
直接后继:当前关键字右侧指针所指⼦树中“最左下”的元素
-
对⾮终端结点关键字的删除,必然可以转化为对终端结点的删除操作
兄弟够借。若被删除关键字所在结点删除前的关键字个数低于下限,且与此结点右(或左)兄弟结
点的关键字个数还很宽裕,则需要调整该结点、右(或左)兄弟结点及其双亲结点(⽗⼦换位法)当右兄弟很宽裕时,⽤当前结点的后继、后继的后继 来填补空缺
本质:要永远保证 ⼦树0<关键字1<⼦树1<关键字2<⼦树2
兄弟不够借。若被删除关键字所在结点删除前的关键字个数低于下限,且此时与该结点相邻的左、右兄弟结
点的关键字个数均=⌈m/2⌉ − 1,则将关键字删除后与左(或右)兄弟结点及双亲结点中的关键字进⾏合并
在合并过程中,双亲结点中的关键字个数会减1。若其双亲结点是根结点且关键字个数减少⾄0(根结点关键
字个数为1时,有2棵⼦树),则直接将根结点删除,合并后的新结点成为根;若双亲结点不是根结点,且关
键字个数减少到 ,则⼜要与它⾃⼰的兄弟结点进⾏调整或合并操作,并重复上述步骤,直⾄符合B
树的要求为⽌
6. B+树
6.1 B+树定义
⼀棵m阶的B+树需满⾜下列条件:
1)每个分⽀结点最多有m棵⼦树(孩⼦结点)。
2)⾮叶根结点⾄少有两棵⼦树,其他每个分⽀结点⾄少有 ⌈ m / 2 ⌉棵⼦树。
3)结点的⼦树个数与关键字个数相等。
4)所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按⼤⼩顺序排列,并且相邻叶结点按⼤⼩顺序相互链接起来。
5)所有分⽀结点中仅包含它的各个⼦结点中关键字的最⼤值及指向其⼦结点的指针(并不包含指向相应记录的指针)。
要追求“绝对平衡”,即所有⼦树⾼度要相同
对⽐:分块查找(块内⽆序、块间有序)
6.2 B树和B+树的比较
(1)m阶B树:结点中的n个关键字对应n+1棵子树;
m阶B+树:结点中的n个关键字对应n棵子树。
(2)m阶B树:根节点的关键字数n[1,m-1]。其他结点的关键字数n[[m/2]-1,m-1];
m阶B+树:根节点的关键字数n[1,m]其他结点的关键字数n[[m/2],m]。
(3)m阶B树:在B树中,各结点中包含的关键字是不重复的;
m阶B+树:在B+树中,叶结点包含全部关键字非叶结点中出现过的关键字也会出现在叶结点中。
(4)m阶B树:B树的结点中都包含了关键字对应的记录的存储地址;
m阶B+树:在B+树中,叶结点包含信息,所有非叶结点仅起索引作用,非叶结点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
6.2 B+树的查找
6.2.1 查找成功
6.2.2 查找失败
也支持顺序查找
6.3 典型应⽤:
关系型数据库的“索引”(如MySQL)
在B+树中,⾮叶结点不含有该关键字对应记录的存储地址。可以使⼀个磁盘块可以包含更多个关键字,使得B+树的阶更⼤,树⾼更矮,读磁盘次数更少,查找更快