在此之前,我们需要先了解一下二叉排序树和平衡二叉排序树的思想和性质。
m叉排序树
现在我们将二叉排序树升级到—— “m叉排序树”,这个也叫m路查找树。
m叉排序树满足以下性质:
① 结点最多有m棵子树,m-1个关键字,其结构如下图:
其中,n为关键字个数,Pi (0 <= i <= n) 为指向子树根结点的指针,Ki (1 <= i <= n) 为关键字。
② K(i) < K(i+1),1 <= i <= n-1
③ 子树Pi 中的所有关键字均大于Ki,小于K(i+1),1 <= i <= n-1
④ 子树P0中的关键字均小于Ki,而子树Pn中的所有关键字均大于Kn
⑤ 子树Pi也是m路查找树,0 <= i<= n
例子:有一棵 3路查找树,给出查找35的过程。
其查找过程与二叉排序树的查找过程类似。首先找到根结点A,因为35介于20和40之间,因而找到结点C,又因为35大于30,所以找到结点E,最后在E中找到35。
B树
如果一棵 m叉排序树是一棵平衡的m叉排序树,那么可以称之为 B树。
B树满足以下性质:
① 树中每个结点最多有m棵子树。(就是m个指向子树的指针域)
② 根结点至少有两棵子树。
③ 除根节点之外的所有非叶结点至少有 [m/2]棵子树( [m/2]计算:如[3/2]=[1.5]=2,"[ ]"就是结果向上取整)。
④ 所有叶结点出现在同一层上,并且不含信息,通常称为失败结点。失败结点为虚结点,在B树中并不存在,指向它们的指针为空指针。引入失败结点是为了便于分析B树的查询性能。
例子:一棵4阶B树
如果要查找58,首先从A结点开始,因为58>37,所以找到C结点,又因为40<58<85,所以找到结点G,最后在结点G中找到58.(查找过程与m叉排序树类似,它们的数据结构也类似)。
B树的插入过程
下面是一棵简易的3阶B树:
如果要插入一个49,依据之前B树查找算法,可以找到要插入到G结点,并且是在50前面。
然后我们发现G结点一共有四个指向字结点的指针域,不符合3阶B树的性质(3阶B树只能有三个分支),所以要对G结点进行分裂。
以中间关键字为分界线,左边部分保留在原结点中,右边部分要分到新的结点G’中。中间那个关键字则插入到父结点合适的位置。结果如下:
然后我们也发现C结点也需要分裂。以关键字53为中间,左边部分保留在C结点,右边部分保留在新结点C’,53插入到父结点合适的位置。结果如下:
B树的删除过程
1.在最下层结点删除一个关键字
有一棵4阶B树:
1️⃣ 当最下层结点中的关键字数大于 [m/2]-1 时,可直接删除。
如删除11时,13与其右的指针左移即可,如图:
在此基础上,再删除53后
2️⃣ 当最下层待删除的关键字所在结点中关键字数目为最低要求 [m/2]-1时,如果其左(右)兄弟中的关键字数目大于 [m/2]-1,则可采用“父子换位法”。
在删除11,53后,再删除39时,为了保持其“中序有序”,可将父结点中的43下移到39处,而将右兄弟中最左边的47上移至原43处。如图:
3️⃣ 当最下层待删除结点及其左右兄弟中的关键字数目均为最低要求数目 [m/2]-1时,需要进行合并处理,合并过程与插入过时的分裂过程“互逆”,合并一次,分支数减少一,可能出现“连锁合并”,各分支深度同时减1。
比如删除64后,为保持各分支等长(平衡),将删除64后的剩余信息及将78并入右兄弟(当然也可以将删除后的剩余信息及47与左兄弟合并)。
2.在非最下层结点中删除一个关键字
还是这个4阶B树,
如果删除43,在保持“中序有序”的前提下,可将43“右子树”中的最小值(“左下端”)顶替43,而后在“左下端”中删除47,结果如下:
再删除35,如果用35“左子树”的“右下端”元素27代替35,那结果如下:
然后再删除“右下端”中的27,最终结果如下:
B+树
待续。。。。。