B+树的特性
B+树和B树一样都是多路平衡树,也叫多叉树。两者的性质也基本一致。
B+树大部分特性都和B树一样,唯一不同的只有以下几点:
- 所有的数据都存储在叶子节点,中间节点不存放数据
- 中间节点的元素数量和子树数量一致,而B树子树数量比元素数量多1
- 叶子节点是一个链表,可以通过指针顺序查找
从上图我们可以看到,所有出现在中间节点的元素都能在叶子节点当中找到,这对应了刚才说的所有数据都存放在叶子节点当中。然后我们还可以发现,中间节点当中的元素数量和子树数量一致,同样对应了区间分割。父节点的每个元素对应了一个子树中最大的元素。
最后的链表,是链表出现在树当中。
B+树的查
由于B+树当中所有的数据都存储在叶子节点,所以我们在查找的时候,必须要一直查找到叶子节点为止。也就是说不会再有中途退出的情况,这样就简化了我们的判断,几乎不再需要临时退出了。
另一个特性是B+树当中的元素数量和子树数量一致,并且每个元素都代表一棵子树当中的最大值。通过这个限制,我们可以很轻松地确定我们要查找的元素究竟在哪棵子树当中。而B树则有可能出现超界的情况,我们需要特殊判断。
另一个特性是B+树当中的元素数量和子树数量一致,并且每个元素都代表一棵子树当中的最大值。通过这个限制,我们可以很轻松地确定我们要查找的元素究竟在哪棵子树当中。而B树则有可能出现超界的情况,我们需要特殊判断。
举个例子,这是一棵B树:
假设我们查找的元素是12,我们在根节点当中判断,先通过二分查找查找到9,发现12 > 9,于是我们去最右侧的子树当中检查。
而如果是B+树,会是这样,为了作图方便,我省去了叶子节点中横向的指针。
可以看到我们直接二分就可以精准地找到对应的子树,我们直接往下递归就好了。如果超界了,则说明肯定不在树上,可以提前返回。
B+树的增
添加元素,添加元素的逻辑和B树基本一致,只是有些细微的变动。
和B树一样,B+树的所有插入操作也都发生在叶子节点。所以我们通过查找操作找到适合插入这个元素的节点,进行插入。由于B+树对节点上元素的数量进行了限制,最多不能超过M个,也就是说插入操作可能会引起非法,所以我们需要对这种情况进行判断,如果插入会导致非法,我们需要采取措施维护数据结构的性质。
采取的措施很简单,和B树一样,如果节点元素数量超标,那么进行分裂。
采取的措施很简单,和B树一样,如果节点元素数量超标,那么进行分裂。
假如这是一颗4阶的B+树,那么当我们插入一个元素之后,它就会发生分裂。比如我们插入5,可以得到:
注意到了吗,由于分裂会产生两棵子树,所以分裂之后上层的节点会有两个元素,而B树只有一个元素。
但如果分裂不是发生在根节点,那么还是只会上传一个元素,和B树一样。
再来看一个例子:
如果此时我们加入元素12,第三个子树的元素数量会超标,于是会发生分裂:
我们最后一个节点插入元素之后发生了分裂,但是只上传了10进入了父亲节点当中,因为15本来就已经在父节点当中了,没有必要上传。
当然这个图不是最终的形态,根节点显然也超过了限制,需要进一步分裂,最后变成这样:
B+树的删除
B+树的删除逻辑和B树完全一致,只是具体操作的细节有略微的区别。
和B树一样,所有的删除全部都发生在叶子节点,但是由于B+树所有的元素都存在叶子节点,所以我们不存在需要考虑删除中间节点并且用后继来替代的情况。我们直接查找到叶子节点进行删除和维护即可。不得不说这一点上要简化了许多。
直接删除
和B树一样,如果叶子节点元素数量富裕,那么直接删除。
兄弟节点富裕
兄弟节点富裕,很简单我们和兄弟节点借就行,和B树不一样,由于所有的节点都在叶子上,我们可以直接将兄弟节点的元素借过来,但是需要注意更新父亲节点中的值。
举个例子,比如说我们要删除下图中的4,我们发现它的左兄弟有富裕的元素,我们不再需要通过父节点中转,可以直接借过来。
但是借过来之后,会有一点小问题,就是父节点中的3就失效了,因为它不再是左侧子树中最大的元素了,最大的元素变成了2。所以我们需要更新父节点中这个值。
兄弟节点不富裕
最后,我们来看下兄弟节点也不富裕的情况。这点和B树类似,但是略有不同。在当下场景当中,我们不再向父节点强行借元素了,想想也能明白,父亲节点当中的数据都在叶子上,还有什么好借的呢?所以我们不借元素,直接和兄弟节点合并。
比如在下图当中,如果我们要删除元素12,会导致违反限制,这个时候我们直接合并红框当中的两个节点。
合并之后会带来子树的数量减少,所以我们要remove掉父节点当中的一个元素。我们对照一下上图,可以发现,我们要删除父节点当中的10。这里我们要判断一下,如果和当前节点合并的是左兄弟,那么我们要删除的是左兄弟的最大值,否则是右兄弟的最小值。删除之后,维护父节点,我们发现父节点不再满足条件,需要维护。
显然它没有富裕的兄弟节点,于是我们继续合并:
同样的操作之后,我们发现当前节点的父节点也就是根节点只剩下了一个元素,这是非法的,于是我们需要将它抛弃,更新根节点。
转自:https://mp.weixin.qq.com/s?src=11×tamp=1587194052&ver=2285&signature=dwV6E-qhGrU6UF8p52w4Jqq66UXvsccDIxbGAfczSKjwy7Pp36CraD6h7wYKga0Bo2QNjRE3tvU6ZSbB3MhqcDBuKcpXzbVJdWf1cTv6JH394gxFijs0lCncUP2vUvdB&new=1