第一种情况:该结点的双亲都是2结点,且拥有一个3结点的孩子,如下图,删除结点1,这种情况只需要左旋,6成为父亲节点,4成为6的左孩子,7成为6的右孩子。
第二种情况:该节点的双亲是2结点,右孩子也是2结点,如下图,删除4,左旋导致没有右孩子,不满足B树定义的第一条,所以这个时候需要对树的结构进行调整,首先将7进行左旋,将8进行左旋,9也进行左旋,形成图三所示的样子。
第三种情况:该结点双亲是一个3结点,如下图所示,删除10结点,12、14不能成为3结点,于是将此结点拆分,并将12、13合并成左孩子。
第四种情况:满二叉树的情况,删除任何一个叶子都会使整棵树不能满足第一条的定义,如下图所示,当删除8的时候,需要考虑将整棵树的层数减少,这个时候将6、7合并为9的左孩子,这个时候不满足第4条定义,需要将9、14合并为,最终形成右图所示。
3.删除的元素元素如果不是叶子结点,则考虑按照中序遍历后得到的该元素的前驱或者后继来进行替换该结点。这里我们就不上图来说明了。
接下来我们做一些思考,由二叉树查找、二叉树排序树、平衡二叉树、B树我们能得出一个结论,树的高度越小查找的速度越快,当元素的数目一定的时候,怎么样才能使树的高度降低,当然是扩展树的阶数才能降低树的高度,之前很火的什么4Y个URL中查询某个URL等之类的题,我想看到这里的时候大家会有一些想法了吧,等等以后我会对类问题进行一次总结,这次先不谈了。
再来做一个思考,对于n个关键字的m阶树,最坏需要需要几次查找?
1.因为根至少有两个孩子,因此第2层至少有两个结点。
2.除去跟结点和叶子外,每个分支的结点至少都有m/2个孩子;
3.第三层结点的个数2*m/2个节点;
4.第四层结点的个数2*(m/2)^2个结点;
5.第K+1层结点的个数2*(m/2)^k-1个结点,这里K+1层就是叶子结点;
6.B树有n个关键字,当你找到叶子节点,就等于查找不成功的结点为n+1,因此n+1>=2*(m/2)^k-1,即k<=log(m/2)((n+1)/2)+1;
7.结论根结点到关键字结点涉及的结点个数不操过k<=log(m/2)((n+1)/2)+1;
总结如下,一颗n个关键字结点的m阶树最大高度为k<=log(m/2)((n+1)/2)+1,B树的每个非根的分支节点m个孩子,其中 m/2 <= m<= m,随着m的增加,B树的高度下降,查找性能提升;
B+树:
接下来我们继续探讨一个问题,看下图,当我们对B树进行中序遍历的时候,假设每一个结点都在硬盘不同的页面上,这个时候必然会经过页2---页1--页3--页4--页1---页4---页1---页5,这样会导致每次那一个元素都要对结点的元素进行遍历,我们有没有什么办法让元素只被访问一次,带着这个问题我们来看下B+树。
接下来我们还是老套路看图说话,下图是一颗B+树,其有如下特点:出现在分支结点中的元素会被当作分支结点位置的中序后继结点再次列出,另外每个叶子结点都会保存一个执行后一个叶子结点的指针。
一颗m阶树的B+树和m阶的B树差异如下:
1.n颗子树的结点包含n个关键字;
2.所有的叶子结点包含全部关键字的信息,以及指向这些关键字记录的指针,叶子结点本身按照从小到大的顺序进行排列;
3.所有分支结点都可以看成索引,结点中含有子树的最大(或最小)关键字。