一、简介
名称:平衡多路查找树
出现的原因:为了解决 平衡二叉树在存储大量数据时的树过高的问题。
主要使用场景:数据库索引
二、定义与性质
基础1:B树节点的结构
1)叶子结点结构
叶子节点里面什么都没有,但它在逻辑上确实是一个结点。可以把它想象成一个空盒子。
2)非叶结点(即不是叶子结点的结点)结构
指针比key(关键字)多一个(多一个指针0),因为key是夹在指针之间的。
这里的“结点”指的是,它是构成B树的基本单元,在图中表示为这个淡黄色的圆角矩形。即B树是由多个该结构的结点构成的,这些结点的n可能等于任意正整数。它的形状就是下图这样。
基础2:B树的阶
B树中所有节点的孩子结点数(节点的指针数)的最大值称为B树的阶。下图为一棵3阶B树,因为该树的所有结点中,单个节点拥有的孩子结点数(指针数)最多为3
即key“78”和key“88”所在的节点,拥有三个子树(三个指针)。
基础3:终端结点
终端结点是叶子节点的父节点
性质1
在一棵m阶B树中,树中单个节点最多有m棵子树(即最多有m-1个key)
解析:这其实和“阶的定义”是对应的。换句话说就是:m阶B树单个节点最多有m个指针。那么那个拥有m个指针的结点自然也就有m-1个key了。
注意:这里的“最多”(至多),即:子树最多的那个节点,子树数目可以比m小。性质1限定了B树结点内部的最大规模。
性质2
若根结点不是终端结点,则至少有两棵子树。
解析:该B树
若根结点是终端结点
若根结点不是终端结点
但是
性质3
除根结点之外的所有非叶节点至少有[m/2]棵子树。
解析:即图中绿色圆角矩形中的每个结点都至少要有[m/2] (m除以2,并向上取整)棵子树。
例如:这里是一棵3阶B树,这里图中绿色圆角矩形中的每个结点都至少有([m/2]=[3/2]=[1.5]=1.5向上取整=2) 2棵子树。可以看到,这里是符合的。
性质3限定的是B树单个结点的最小规模。(性质1限定的是B树单个结点的最大规模)
性质4
非叶节点的结构
解析:节点上的指针指向的结点的key与当前结点的key,这些值从左到右是以从小到大的顺序排列的。
即:设p1v表示指针1指向的结点的最大的key;k1表示key1的值,则有:p0v<k1<p1v<k2<p2v<…<kn<pnv
如该例子中的:12<15<20<22<…<75<80
性质5
所有的叶子节点都出现在同一层次上,并不带任何信息
解析:图中红色矩形框中的结点均为叶子节点,看得出它们都在最下层,并不带任何信息,因此用null表示。
三、常用操作
1.插入
B树元素的插入
例如给定如下key【 20,30,50,52,60,68,70 】创建一个3阶B树。
(性质1要求子树数<=3,性质3要求子树数>=2)
插入20:
Ok,满足3阶B树的性质,继续插入
插入30:
Ok,满足3阶B树的性质,继续插入
插入50:
NO,3阶B树最多只能有3个子树,这里有了4棵子树,违反了“性质1”;于是开始进行“结点的分裂”(提出中间位置( [3/2]=2 )的key 30,将其作为独立节点。
Ok,满足3阶B树的性质,继续插入
插入52:
Ok,满足3阶B树的性质,继续插入
插入60:
No,不满足3阶B树的性质1,这里有了4棵子树,违反了“性质1”;于是开始进行“结点的分裂”
Ok,满足3阶B树的性质,继续插入
插入68:
Ok,满足3阶B树的性质,继续插入
插入70:
No,不满足3阶B树的性质1,这里有了4棵子树,违反了“性质1”;于是开始进行“结点的分裂”
No,分裂之后仍然不满足3阶B树的性质1,这里根节点有了4棵子树,违反了“性质1”;于是继续进行“结点的分裂”
Ok,满足3阶B树的性质,完成插入。
小结:插入操作主要涉及到两个操作,一是节点的分裂:中间位置( [3/2]=[1.5]=1.5向上取整=2 )key的踢出,使其作为独立的结点。结点的合并:被踢出的中间key合并到其父节点,也可以看成是中间key先变成了独立结点,再与其父结点合并。
2.结点的删除
删除的关键:要使得删除后的结点中的key个数>=[m/2]-1,因此将涉及到结点的“合并”问题。可以分为关键字在终端结点上和不在终端结点上两种情况。
情况的划分:
1)关键字在终端结点上
1.1 结点内key数量>[m/2]-1,删除key不会破坏其作为B树的结构,可以直接删除。
1.2 结点内key数量=[m/2]-1,并且其左右兄弟结点中存在key数量>[m/2]-1的结点,则去兄弟结点中借key。
注释:从兄弟结点借key“20”,将它独立为一个节点,然后按照规则排列涉及到的节点。
1.3 结点内key数量=[m/2]-1,并且其左右兄弟结点中不存在key数量>[m/2]-1的结点,则需要进行结点合并。
2)关键字不在终端结点上
补充定义:
相邻关键字key:对于不在终端结点上的key,它的相邻key是其左子树中值最大的key和其右子树中值最小的key。注意是子树而不是子节点中。
“20”的相邻key:“19”和“39”
“52”的相邻key:“50”和“60”
需要先转换成在终端结点上,再按照在终端结点上的情况来分别考虑对应的方法。
删除“52”
第一步:待删除key与位于终端结点的相邻key互换位置
第二步:删除换位之后的待删除key