B-tree、B+tree详解(二)插入与删除

B-tree关键字插入操作

生成从空树开始,逐个插入关键字。但是由于B-树节点关键字必须大于等于[ceil(m/2)-1],(其中ceil(x)是一个取上限的函数)所以每次插入一个关键字;首先在最底层(叶子节点那一层)的某个非终端节点中添加一个“关键字”,该结点的关键字不超过m-1,则插入完成;否则要产生结点的“分裂”,将一半数量的关键字分裂到新的其相邻右结点中,中间关键字上移到父结点中。

示例

插入以下字符字母到一棵空的B 树中(非根结点关键字数小了(小于2个)就合并,大了(超过4个)就分裂):

C N G A H E K Q M F W L T Z D P R X Y S

1、首先,结点空间足够,4个字母插入相同的结点中,如下图:

 

 

2、当咱们试着插入H时,结点发现空间不够,以致将其分裂成2个结点,移动中间关键字G上移到新的根结点中,在实现过程中,咱们把A和C留在当前结点中,而H和N放置新的其右邻居结点中。如下图:

 

3、插入E,K,Q时,不需要任何分裂操作:

 

4、插入M需要一次分裂,注意M恰好是中间关键字,以致向上移到父节点中:

 

5、插入F,W,L,T不需要任何分裂操作:

 

6、插入Z时,最右的叶子结点空间满了,需要进行分裂操作,中间关键字T上移到父节点中,注意通过上移中间关键字,树最终还是保持平衡,分裂结果的结点存在2个关键字。

 

7、插入D时,导致最左边的叶子结点被分裂,D恰好也是中间关键字,上移到父节点中,然后字母P,R,X,Y陆续插入不需要任何分裂操作(别忘了,树中至多5个孩子)。

 

8、最后,当插入S时,含有N,P,Q,R的结点需要分裂,把中间关键字Q上移到父节点中,但是情况来了,父节点中空间已经满了,所以也要进行分裂,将父节点中的中间关键字M上移到新形成的根结点中,注意以前在父节点中的第三个指针在修改后包括D和G节点中。这样具体插入操作的完成:

 

B-tree关键字删除操作

首先查找B树中需删除的关键字,如果该关键字在B树中存在,则将该关键字在其结点中进行删除,如果删除该关键字后,首先判断其结点是否有左右孩子结点,如果有,则上移孩子结点中的某相近关键字到父节点中,然后是判断移动之后的情况;如果没有,直接删除后,判断删除之后的情况。

删除元素,移动相近元素之后,如果某结点中关键字数小于ceil(m/2)-1,

(m为阶数)则需要看其某相邻兄弟结点是否丰满

如果丰满,则向父节点借一个元素来满足条件;如果其相邻兄弟都刚脱贫,即借了之后其结点数目小于ceil(m/2)-1则该结点与其相邻的某一兄弟结点进行“合并”成一个结点,以此来满足条件。

(结点中关键字个数大于ceil(m/2)-1除根结点之外的结点的关键字的个数n必须满足: ceil(m / 2)-1)<= n <= m-1。m表示最多含有m个孩子,n表示关键字数.)

示例

最初态5阶B树如下,实现依次删除H,T,R,E。

(关键字数最小为ceil(m / 2)-1=2。最多为m-1=4)

 

1、首先删除关键字H,当然首先查找H,H在一个叶子结点中,且该叶子结点关键字数目3大于最小元关键字数目2,则操作很简单,咱们只需要移动K至原来H的位置,移动L至K的位置(也就是结点中删除关键字后面的关键字向前移动)

 

2、下一步,删除T,因为T在中间结点中,它有孩子,且关键字数目=2,所以需要向孩子借一个关键字W(一般借升序的下个关键字),将W上移到T的位置,然后将原包含W的孩子结点中的W进行删除,这里恰好删除W后,该孩子结点中关键字数目大于2,无需进行合并操作。

 

3、下一步删除R,R在叶子结点中,但是该结点中关键字数目为2,删除导致只有1个关键字,已经小于最小关键字数目ceil(5/2)-1=2,而由前面我们已经知道:如果其某个相邻兄弟结点中比较丰满(关键字个数大于ceil(5/2)-1=2),则可以向父结点借一个关键字,然后将最丰满的相邻兄弟结点中上移最后或最前一个关键字到父节点中(有没有看到[红黑树]中左旋操作的影子?),在这个实例中,右相邻兄弟结点中比较丰满(3个关键字大于2),所以先向父节点借一个关键字W下移到该叶子结点中,代替原来S的位置,S前移;然后X在相邻右兄弟结点中上移到父结点中,最后在相邻右兄弟结点中删除X,后面关键字前移。

 

4、.最后一步删除E, 删除后会导致很多问题,因为E所在的结点数目刚好达标,刚好满足最小关键字个数(ceil(5/2)-1=2),而相邻的兄弟结点也是同样的情况,删除一个关键字都不能满足条件,所以需要该节点与某相邻兄弟结点进行合并操作;首先移动父结点中的关键字(该关键字在两个需要合并的两个结点关键字之间)下移到其子结点中,然后将这两个结点进行合并成一个结点。所以在该实例中,咱们首先将父节点中的关键字D下移到已经删除E而只有F的结点中,然后将含有D和F的结点和含有A,C的相邻兄弟结点进行合并成一个结点。

 

5、也许你认为这样删除操作已经结束了,其实不然,在看看上图,对于这种特殊情况,你立即会发现父节点只包含一个关键字G,没达标(因为非根节点包括叶子结点的关键字数n必须满足于2=<n<=4,而此处的n=1)这是不能够接受的。

如果这个问题结点的相邻兄弟比较丰满,则可以向父结点借一个关键字。然后父亲再向孩子借一个关键字。但是它的兄弟也很贫穷。所以这时只能与兄弟结点进行合并成一个结点,而根结点中的唯一关键字M下移到子结点,这样,树的高度减少一层。

 

至此完成删除操作。

补充一下最后一个关键字删除的另外一种情况:也是5阶B树

 

6、删除关键字C后发现只剩下一个关键字F所以我们打算去借,这个借首先想到孩子,然后再说父亲删除关键字C后D关键字上移到C的位置,但是出现上移关键字D后,只有一个关键字E的结点的情况。且它相邻兄弟结点才刚脱贫(最少关键字个数为2),不可能向父节点借关键字,所以只能进行合并操作,合并操作就要向父结点借一个元素,所以又把D借下来。于是这里将含有A,B的左兄弟结点,加上D关键字和含有E的结点进行合并成一个结点。

7、这样又出现只含有一个关键字F结点的情况,这时,发现其相邻的兄弟结点是丰满的(关键字个数为3>最小关键字个数2),这样就可以想父结点借关键字,把父结点中的J下移到该结点中,相应的如果结点中J后有元素则前移,然后相邻兄弟结点中的第一个关键字(或者最后一个关键字)上移到父节点中,后面的关键字(或者前面的关键字)前移(或者后移);注意含有K,L的结点以前依附在M的左边,现在变为依附在J的右边。这样每个结点都满足B树结构性质。

 

 

B 树如何保证平衡

我们知道,平衡的树之所以能够加快查找速度,是因为在添加、删除的时候做了某些操作以保证平衡。

平衡二叉树的平衡条件是:

  • 左右子树的高度差不大于 1;

B 树的平衡条件则有三点:

  1. 叶子节点都在同一层
  2. 每个节点的关键字数为子树个数减一(子树个数 k 介于树的阶 M 和M/2(向上取整)之间,即:一棵 3 阶的 B 树(即节点最多有三个子树)子树个数范围是:2-3之间,每个节点的关键字数范围是:1-2之间)
  3. 子树的关键字保证左小右大的顺序

也就是说,一棵 3 阶的 B 树(即节点最多有三个子树),每个节点的关键字数最少为 1,最多为 2,如果要添加数据的子树的关键字数已经是最多,就需要拆分节点,调整树的结构。

 

分析下 B 树添加元素时如何保证平衡:

 例如4 阶 B 树:

6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4

 

我们来根据前几步分析下 B 树的添加流程:

首先明确:4 阶 B 树表示每个节点最多有 4 个子树、3 个关键字,最少有 2 个子树、一个关键字

添加 6,第一个节点,没什么好说的

添加 10,根节点最多能放三个关键字,按顺序添到根节点中

添加 4,还能放到根节点中

添加 14,这时超出了关键字最大限制,需要把 14 添加为子树,同时为了保证“所有叶子节点在同一层”,就需要拆几个关键字作为子树:

 

拆为-->

 

这个拆的过程比较复杂,首先要确定根节点保留几个关键字,由于“非叶子节点的根节点至少有 2 棵子树”的限制,那就至少需要两个关键字分出去,又因为“子树数是关键字数+1”,如果根节点有两个关键字,就得有三个子树,无法满足,所以只好把除 6 以外的三个关键字都拆为子树。

 

谁和谁在一个子树上呢,根据“左子树比关键字小、右子树比关键字大”的规律,4 在左子树,10 和 14 在右子树。

继续添加 :

添加 5,放到 4 所在的子树上

添加 11,放在 10 和 14 所在的右子树上

添加 15,按大小应该放到 10、11 和 14 所在的子树上,但因为超过了关键字数限制,又得拆分

 

因为“根节点必须都在同一层”,因此我们不能给现有的左右子树添加子树,只能添加给 6 了;但是如果 6 有三个子树,就必须得有 2 个关键字,提升谁做关键字好呢,这得看谁做 6 中间的子树,因为右子树的所有关键字都得比父节点的关键字大,所以这个提升的关键字只能比未来右子树中的关键字都小,那就只有 10 和 11 可以考虑了。

提升 10 吧,没有比它小的做子树,那就只能提升 11 了:

 

再添加元素也是类似的逻辑:

首先考虑要插入的子树是否已经超出了关键字数的限制。

超出的话,如果要插入的位置是叶子节点,就只能拆一个关键字添加到要插入位置的父节点。

如果非叶子节点,就得从其他子树拆子树给新插入的元素做孩子。

删除也是一样的,要考虑删除孩子后,父节点是否还满足子树 k 介于 M/2 和 M 的条件,不满足就得从别的节点拆子树甚至修改相关子树结构来保持平衡。

 

总之添加、删除的过程很复杂,要考虑的条件很多,具体实现就不细追究了,这里我们有个基本认识即可。

正是这个复杂的保持平衡操作,使得平衡后的 B 树能够发挥出磁盘中快速查找的作用。

 

参考:

https://www.jianshu.com/p/ac12d2c83708

https://blog.csdn.net/luzhensmart/article/details/88617936#comments

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值