这篇博客主要讲解B树及其插入删除操作,并给出操作的流程图以达到清晰易懂的目的,尽管标题是从二叉排序树到平衡二叉树再到红黑树系列3,没有B树二字,但他们都是动态查找树,所以我将他们归为一个系列。
B树是为磁盘或其他直接存取的辅助存储设备而设计的一种平衡搜索树。它以一种很自然的方式推广了二叉搜索树,B树与红黑树的不同之处在于B树结点的孩子不限于最多为2,而是可以有数个到数千个不定。因为结点的分支更多,因而相比红黑树,高度更小。
B树的定义有如下两种,但殊途同归。
定义形式一(以度 t 定义):
一棵B树T 具有以下性质:
1.每个结点x有下面属性
a x.n当前存储在结点x中的关键字个数。
b x.n个关键字本身,且按非递减顺序排列
c x.leaf 布尔值 如果x是叶子结点,则为TRUE,否则为FALSE
2.每个内部结点x还包含x.n+1个指向其孩子的指针。(x.n个关键字将一个区间分为x.n+1个子区间,所以有x.n+1个孩子,孩子数=关键字数+1)。叶结点没有孩子,所以它们的没有指向孩子的指针。
3.x.n个关键字对存储在各子树中的关键字范围进行分割。
4 每个叶节点具有相同的深度。即树的高度h
5 每个结点包含的关键字的个数称为度,最小度数t>=2;
a 根结点至少有一个关键字,除了根节点,其他每个结点必须至少(t-1)个关键字
b 每个结点至多包含(2t-1)个关键字,即一个内部结点最多可以有2t个孩子,当一个结点恰好有2t-1个关键字时,称该结点是full的。
struct BNode
{
int num;//关键字个数
int key[maximun];//结点中存放的关键字
struct BNode *parent;//指向双亲结点指针
struct BNode *ptr[maximum];//关键字的孩子结点指针数组
}
例如 t=2 的B树最简单,每个内部结点可以有2,3,4个孩子,即为一棵2-3-4树。下面给出 t = 2 表示{1,2,3,4,5}的所有合法的B树。
根结点关键字个数最少为1个,最多为3个(显然根有3个关键字,则其要有4个孩子,至少4个关键字,一共7个关键字>5) 其余结点至少1个关键字,至多3个关键字。
一棵包含n个关键字,高度为h 最小度数 t >=2的B数 有如下性质:
<注意:树高h是从1开始算起,即只有根节点的B树高度h=1,与算法导论略有不同>
可以得到:对于一共含有n个关键字 度为 t 的 B树,其高度上限为log(t)((n+1)/2)+1 ,可见 B树是一个非常有效率的数据结构。
下面给出B树定义形式二(按阶定义):
一棵 m 阶的B树满足以下条件:
1 每个结点至多有m棵子树 <==该结点至多m-1个关键字>
2 除根结点,其他分支结点至少有[m/2](取上界)棵子树 <==至少[m/2]-1个关键字>
3 根结点至少有2棵子树
4 所有叶子结点在同一层,叶子结点不含任何关键字信息
5 有 j 个孩子的非叶子结点恰好有j-1个关键字
事实上,按度定义是从B树结点的关键字个数出发 按阶定义是按B树结点的子树个数出发,都是一样的。
下面主要介绍B树的插入和删除操作:
B树关键字的插入:首先要考虑的是待插入关键字是否存在,若不存在,在查找的过程中就会找到插入结点的位置。插入时主要要考虑的待插入结点是否有足够的空间将关键字插入进去(插入后还要移动元素以保证升序有序)。如果结点空间已满,这时就要分裂结点。并将分裂前结点的中间关键字去除上移给双亲结点。如果双亲结点已满,则需要再度分裂。 最坏的情况是 插入一个关键字,却需要一直分裂到根结点,再新增一个结点,整个B树增加了一层,高度h=h+1;
具体流程图如下:
B树关键字的删除:在删除一个关键字时,首先要找到关键字的位置,并考虑删除后该结点关键字的大小是否满足B树的要求 和 对关键字左右子树的影响。流程图中非删除关键字所在结点是否为叶子结点分类讨论。必要时,要合并子树。最极端的一种情况,因为删除一个关键字,从下往上合并结点,最后导致B树的高度h=h-1.
具体流程图如下:
关于B树插入删除关键字的几点补充说明:
1 关键字的插入 一定是在叶子结点,所以不需要考虑对子树影响,只需要考虑结点可能超过容量,因而要分裂结点。而删除可能发生在非叶子结点,所以要考虑对子树影响,且结点删除一个关键字,可能小于关键字数量下限,因而要合并结点。
2 分裂结点时,父结点的子树多了一个,因而父结点的关键字也要多一个,将待分裂结点中间值上移给父结点,正好划分为两部分且结点容量满足数量要求。
3 删除关键字时,结点关键字个数达到下限,并不急着合并结点,而是先间接向关键字数量较多(较多:借了之后不会导致到达关键字下限)的相邻兄弟结点借关键字(实际过程为向父结点借一个,父结点的缺失由相邻兄弟结点上移一个关键字补充,此为间接之含义,为什么不直接向兄弟结点借关键字,是因为保证关键字有序,兄弟结点如果是右边兄弟关键字大于父节点关键字的)。只有当相邻结点关键值数量都为下限,不能外借时,才会考虑合并。
4 合并结点时,父结点的子树少了一个,因而父结点的关键字也要少一个;因而父节点的一个关键字下移到合并结点中。此时合并结点的关键字数量为(t-1)+1+(t-2)=2t-2
刚好小于每个结点关键字数量上限 (2t-1)。 父节点下移一个关键字后是不是达到下限,如果是,又要重复上述操作(此时,相当于父节点删除了一个元素),回到说明3.
5 插入时达到上限的分裂是必然的,而删除时达到下限的合并不是首先要考虑的,尽量外借关键字以维持B树结构。
B+树
尽管B树的查询效率很高,但是并没有解决元素遍历的效率低下问题,为了解决这个问题,出现了B+树,B+树只需要遍历叶子结点就可以实现整棵树的遍历,特别适合数据库中基于范围的查询。另外对于B+树,所有关键字信息都存储在叶子结点,所以关键字查询的路径长度相同,比较稳定。
B+树是应文件系统所需而出现的一种B树的变形树。一棵m阶的B+树和m阶的B树的区别是:
1 有n棵子树的结点中含有n个关键字
2 所有叶子结点中包含了全部的关键字信息,以及指向这些关键字记录的指针,且叶子结点本身以关键字大小升序排序连接
3 所有非叶子结点可以看为索引部分,结点中仅含有其子树中的最大(或最小)关键字。
参考文献:《算法导论》
http://blog.csdn.net/v_JULY_v/article/details/6530142/
转载请申明:http://blog.csdn.net/u010498696/article/details/46236119