一颗m阶的B树,这里阶的意思是指每个节点拥有的子节点个数,会满足以下几点:
(1)每个节点最多有m个子节点
(2)除了根节点和叶子节点之外,其它的每个节点最少有m/2(向上取整)个子节点
(3)根节点至少有两个子节点,(除了第一次插入的时候,此时只有一个节点,根节点同时是叶子节点)
(4)所有的叶子节点都在同一层
(5)有k个子节点的父节点包含k-1个key关键字
初次接触B树的时候有两个问题比较疑惑,根节点为什么最少要有两个子节点?其它的节点为什么又是最少有m/2个子节点?直到看到这篇博客之后茅塞顿开。简单来讲B树的上述几个条件就是为了维持树的平衡。
下面举个例子来说明一下,所有的图片都是来自于Data Structure Visualization网站,可以保证图片的正确性。
我们依次往一颗B树里边插入递增的数字来观察树形状的变化。
第一个例子,m=3,一颗三阶的B树,依次插入1,2,3。
当3这个key插入的时候,首先试图插入根节点,但是此时根节点总共有4个子节点(下图4个指向子节点的childPtr),不满足条件1,即子节点数量不能超过3,于是根节点要分裂。
根节点一分为三,分裂之后是这样的:
分裂的时候,中间的以中间的key-2为中心,将keys分成三份,中间的key作为新的根节点;左边的部分作为新根节点的左子树,同样的道理右边的部分作为右子树。
也就是说中间的key成为根节点后,至少两个子节点,这就是条件3,这就是根节点为什么至少两个子节点的原因。
我们继续往b树添加key。
当5加进去的时候,最右边叶子节点不满足条件,分裂。
加入6
当7进去的时候,会导致最右边的叶子节点不满足要求,于是叶子节点右开始分裂
当key(6)上移到它的父节点之后又会导致父节点不满足要求,继续分裂。
最后的b树如下,此时树的高度变为了3.
可以这么理解,随着数据的不断加入,树的高度就会慢慢变高,b树的层数增加是通过分裂根节点完成的。
当根节点要分裂的时候,为什么是分裂成左右两个子树而不能分裂成超过两个子树呢?这是由于条件2:除了根节点和叶子节点之外,其它的每个节点最少有m/2(向上取整)个子节点。如果分成三个子节点,那么每个子节点的包含的节点数就是m/3,不满足条件2。
再举一个例子
下面再建个7阶的b树,依次加入1,2,3,4,5,6,7。
当7加入的时候,根节点共有8个child指针,不满足条件1,所以要分裂,这种情况就以中间的key(4)作为新的根节点,1,2,3作为新的根节点的左子节点,5,6,7作为右子节点。变成这个样子:
第三个例子,这个例子其实重复了,只是多举点例子加深理解
下图是根节点第二次分裂的情况,这是一颗5阶b树,保存1到16的key,目前高度为2
当我们往这颗b树加入17的时候,最右边的叶子有6个childPtr,不满足条件1。所以这个节点也要执行分裂,的中间的key(15)要上移到父节点,同时分裂为两个节点,此时它的父节点就是根节点。但是分裂之后就会导致根节点不满足要求,此时根节点有6个子节点。
由于根节点不满足要求,所以根节点也要执行分裂操作。
还是以中间的key作为新的根节点,左右两边分别作为左右子树,变成这个样子:
这时候新的根节点还是两个子树,通过上面的几个例子很容易理解根节点为什么最少要有两个子节点