2-3树
关键字 1-2 个
子节点 2-3 个
平衡树:子节点的高度一致
B树(Balacned Tree)
一种平衡的多分树
平衡:所有的叶结点在同一层,所以每个子节点的高度一致
m阶B树的结构定义
1、每个结点至多有m个子结点
2、除根节点和叶节点外,其他每个结点至少有m/2(向上取整)个结点
3、根结点至少有两个子结点
- 唯一例外的是根节点就是叶结点时没有子结点
- 此时B树只包含一个结点
4、所有的叶结点在同一层(平衡)
5、有k个子节点的非根节点恰好包含k-1个关键码
B树的性质
1、树高平衡,所有叶结点都在同一层
2、关键码没有重复,父结点中的关键码是其子结点的分界
3、B树把(值接近)相关记录放在同一个磁盘页中,从而利用局部性原理
4、B树保证树中至少有一定比例的结点是满的(每个结点有至少有m/2个子节点,半满)
- 这样能改进空间的利用率(如果每个节点只有1个子节点,BST,树高会很高,空间利用率低)
- 减少索引和更新操作的磁盘读取数目(如果每个节点有m个子节点,全满,那么每次插入的时候没处插了)
- 除根节点和叶节点外,M阶B树每个节点有m/2~m个结点,根节点至少有两个子节点(插入时多次分裂)
- 有k个子节点的非根节点恰好包含k-1个关键码
- 父结点中的关键码是其子结点的分界
B树的节点结构
B树的一个包含j个关键码,j+1个指针的节点的一般形式为:
P0,K1,P1,K2,P2....Kj,Pj #j个关键码,j+1个指针指向子节点
其中ki是关键码值,K1<K2<….<Kj
Pi是指向Ki到Ki+1之间的关键码的子树的指针
- 如果有j个关键字,那么有可以表示j+1个区间,即有j+1个子节点
- 关键词下标从1开始,P从0开始,Pi是指向Ki到Ki+1之间的关键码的子树的指针
- 举个例子:P0指向K0到K1之间,即小于K1的,P1指向K1到K2
![](/qrcode.jpg)
B树结点抽象数据类型
class BNode{
int n;//子节点的个数
BNode<Key> *parent;//指向父节点的指针(可有可不有)
Key key[MAXREC];//存储关键码的数组,最多有MAXREC个关键码
BNode<Key> *ptr[MAXREC+1];//指向子节点的指针,最多有MAXREC+1个指针
}
B树的查找
1、把根节点读出来,在根节点所包含的关键码K1…Kj中查找给定的关键码值。当关键码不多时,就用顺序检索,当节点包含的关键码较多时,可以用二分检索。如果找到则检索成功
2、否则,确定要查的关键码指在某个Ki和Ki+1之间,那么去Pi所指向的节点继续查找
下图为查找24的过程
如果树高为h,则查找时访问外存的次数是h
注意:
每个关键码表示索引,找到索引后,每个关键码还对应一个指向外存的指针,这样通过索引才可以访问到完整的记录
B树的插入(分裂向上生长)
情况1:
首先判断关键码14是否存在,按照图中的轨迹查找,此时到15所在的节点,遍历后发现没有14,则在该节点中插入
此时外存读的次数是3,写的次数是1
情况2:
插入可能导致B树朝着根的方向生长,即当插入位置超过关键字的最大个数,节点需要进行分裂
如插入55,但2-3树要求关键字数只由1~2个,此时插入节点50,52已经达到个数,55无法直接插入,需要进行分裂
50 52 55如何拆分?
父节点需要增加一个节点进行区分子节点拆分后的节点,采用二分的方法,将中间的52送到父节点,得到结果:
此时读的次数3,写的次数3(申请两个节点,并分别写入,这里包含两次写,还有在父节点中写入1次)
情况3:
多次分裂,树向上生长
最终的结果是:
B数的删除(向左或向右合并)
6阶B树删除45
对于6阶B树,子节点树要求m/2~m,即 3~6,关键码的个数为2~5,在删除的过程中需要防止下溢出,即关键码的数量小于规定的个数
在删除掉45后,当前节点只有110一个关键字,下溢出了。其中一种方案是向左右节点借关键码,同时要将父节点拉下来,这里演示向右节点借关键码。此时合并的关键码有110,112,135,143,212,需要进行分裂,根据二分分裂的方式,将135移到父节点,即用135替换123,然后子节点分裂,反别是110,112和142,212,结果是:
给出一个极限情况下删除的例子:
删除的时候如果发生下溢出,即当前关键码个数小于定义的个数,则需要进行合并,向左节点或有节点合并
如果删除后,节点为0仍保持0的状态,按照向左向右合并的方式进行操作
B+树
B+树是B树的一种变形,是在叶结点上存储信息的树:
- 所有的管家那么均出现在叶结点上
- 各层节点中的关键码均是下一层相应节点中最大关键码(或最小关键码)的复写
B+树的结构定义
- 每个节点至多有m个子节点
- 每个结点(除根外)至少有m/2(向上取整)个子结点
- 根节点至少有两个子结点
- 有k个子结点的必有k个关键码(这个是与B树的差别)
- 根节点有2~m个子节点
- 除根节点外有m/2(向上取整)~m个子结点
- 有k个子结点的节点有k个关键字(这是与B树的结构差别,B树有k-1个关键字)
- m阶B+树有m个子节点,也有m个关键字
- m个关键字是子结点的复写,当前层的关键码是其每个子结点关键码的最大关键码(也可以是最小)
- 根节点是线性索引,可以顺序查找;再加上多分树形查找
- 树形索引中不包含指向真实数据位置的指针,必须索引到树的叶子节点才算找到;好处是可以提高树的阶数,降低树的高度,加速索引速度
B+树的插入
如下3阶B+树,节点最多有3个关键码,当插入15后,查询到10 23这个节点,插入15。父节点不需要重写
3阶B+树插入16后,叶子节点的关键为16超过最大阶3,所以需要分类成10、15和16、23,并增加分类节点之间的线性链接,修改父节点的关键码,每个关键码是子结点关键码最大值的复写
多次分裂
B+树的删除
如下B+树删除23,首先需要查找23;第一个节点判断,23<=35,到第二个子结点查找;23<=23,在第一个子结点中查找;删除关键码23;
此时父节点中的23能够正确标识父节点的区间,所以不进行更新(类似线段树中的延迟更新)
B树与B+树的区别
节点结构:
- 当前节点有k个子结点,B树有k-1个关键码,B+树有k个关键码
- B树当前节点的关键码类似BST是子结点关键码的分割,B+树当前节点的关键码是子结点的复写(子结点关键码的最大值或最小值)
查找:
B树每个节点保存了指向真实位置的指针,找到关键码就能找到真实位置;B+树必须索引到叶子节点;B+树提供了索性查找和顺序查找两种方式
实际中使用B+树特别多
参考
第10章 索引——2(B树,B树) 标清
https://www.youtube.com/watch?v=vBD6Ve8VryY
原文:大专栏 5、B数、B+数、红黑树