【Java高级数据结构】B树

B树

又称B-树,一棵M阶的B树是一颗M路搜索树,它或者是一棵空树,或者是满足下列性质的树:

  • 1、根结点子女数(子树的个数)为[2, M];
  • 2、除根结点和叶子结点以外的所有分支结点至少有[(M/2), M]个子女(子树);
  • 3、所有的叶结点都位于同一层。

M阶B树的结点结构如下:

n, S0, (K1, S1), (K2, S2), ..., (Kn, Sn)

其中,Si 是指向子树的指针,0 <= i <= n < M;Ki是关键码,1 <= i <= n < M。Ki < K(i+1),1 <= i < n。

  • 在子树 Si 中所有的关键码都小于 K(i+1),且大于 Ki,0 < i < n。
  • 在子树 Sn 中所有的关键码都大于 Kn;
  • 在子树 S0 中的关键码都小于 K1;
  • 子树 Si 也是 M 路搜索树,0 <= i <= n。

事实上,在B树的每个结点中还包含有一组指针D[m],指向实际对象的存放地址。K[i]与D[i] (1 <= i <= n < m) 形成了一个索引项(K[i],D[i]),通过K[i]可找到某个对象的存储地址D[i]。

B树的特点

  • 根结点关键字个数是:[1, (M-1)];非根结点的关键字个数是:[(M/2), (M-1)]
  • 关键字集合分布在整棵树中;
  • 任何一个关键字出现一次,且只出现在一个结点中;(关键字不允许重复)
  • 搜索有可能在非叶子结点结束;
  • 其搜索性能等价于在关键字全集内做一次二分查找;

B树的性质

1、如果已知M路搜索树的度m和它的高度h,则树中的最大结点数为:在这里插入图片描述
2、每个结点中最多有 m-1 个关键码,在一棵高度为 h 的 m 路搜索树中关键码的最大个数为:m^(h+1) - 1

3、设在 m 阶B树中,失败结点位于第 h 层。在这棵B树中关键码个数 N 最小能达到多少?:N >= 2 * (m/2)^(h-1) - 1

4、反之,如果在一棵 M 阶B树中有 N 个关键码,则所有的非失败结点所在层次都小于 h,则:h-1 <= log(m/2)((N+1)/2) 即 h <= log(m/2)((N+1)/2) + 1

5、示例:

  • a):若B树的阶数M=199,关键码总数N=1999999,则B树的高度 h 不超过:log(100)(1000000) + 1 = 4
  • b):若B树的阶数M=3,高度h=4,则关键码总数至少为:N = 2 * (3/2)^(4-1) - 1 = 15

6、M值的选择(M一定是奇数)

如果提高B树的阶数M,可以减少树的高度,从而减少读入结点的次数,因而可减少读磁盘的次数。事实上,M受到内存可使用空间的限制。当M很大超出内存工作区容量时,结点不能一次读入到内存,增加了读盘的次数,也增加了结点内搜索的难度。M值的选择:应使得在B树中找到关键码key的时间总量达到最小。这个时间由两部分组成:

  • a):从磁盘读入结点所用的时间。
  • b):在结点中搜索key所用的时间。

根据定义,B树的每个结点的大小都是固定的,结点内有M-1个索引项(Ki, Di, Pi),1 <= i < M。

在这里插入图片描述

B树的结构设计

public class BTree {
	private static final int M = 5;
	private static final int MaxSize = M - 1; //结点的最大key_value个数
	private static final int MinSize = M / 2; //结点的最小key_value个数
	static class ElemType{
    		private char key;
    		private Object recPtr;
    		public ElemType(char kx){
        		key = kx;
    		}
	} //key_value
	class BNode{
    		private int num = 0;
   	 	private BNode parent = null;
    		private ElemType[] data;
    		private BNode[] sub;
    		public BNode(){
        		data = new ElemType[M];
        		sub = new BNode[M];
    		}
	}
	private BNode root;
	public BTree(){
    		root = null;
	}
}

【Java高级数据结构】B树_第1张图片

B树的操作

B树的插入操作(B树的分裂过程)
  • 空树则直接新建结点。
  • 如果要插入的结点存在,则不再插入。
  • 如果不存在在该树中且没满直接插入到对应的位置中。
  • 如果不存在该树中且满了则需要进行分裂(有的情况下可能进行多次分裂)。

在这里插入图片描述
【Java高级数据结构】B树_第2张图片

【Java高级数据结构】B树_第3张图片

B树的删除操作(B树的合并过程)
  • 若树空或者树中并没有这个元素,则直接退出。
  • 若删除分支结点,则看该元素的直接前驱的结点树的key值个数是否大于 M/2;
    • 若大于则直接用直接前驱替换该元素,并将直接前驱所在叶子节点的元素删除;
    • 若不大于,则看该元素的直接后继,进行上述操作。此时可以将删除分支结点转换为删除叶子节点。
  • 若删除叶子节点,则看删除后结点中的key值得个数处于[(M/2), M]之间,则直接删除叶子结点元素。
    • 若删除之后,key值得个数小于 M/2,以先左后右得原则向同层兄弟结点中借数。
    • 若借完之后兄弟结点的key值个数处于[(M/2), M]之间,则可以直接借数。
  • 若借完之后兄弟结点的key值个数小于 M/2,则进行合并过程。

【Java高级数据结构】B树_第4张图片

【Java高级数据结构】B树_第5张图片
【Java高级数据结构】B树_第6张图片

【Java高级数据结构】B树_第7张图片

B树中查找元素
/**
 * 查找元素
 */
class Result{
    private BNode pNode = null; //所查找元素所在的子树位置
    private int index = -1; //所查找元素所在下标位
    private boolean tag = false; //所查元素是否存在
}
public Result Find(char kx){
    Result result = new Result();
    BNode ptr = root;
    while (ptr != null){
        ptr.data[0].key = kx;
        int i = ptr.num;
        while (kx < ptr.data[i].key) --i;
        result.pNode = ptr;
        result.index = i;
        if(i > 0 && kx == ptr.data[i].key){
            result.tag = true;
            break;
        }
        if (ptr.sub[i] == null) break;
        ptr = ptr.sub[i];
    }
    return result;
}
递归中序遍历
/**
 * 递归中序遍历
 */
private void InOrder(BNode ptr){
    if(ptr != null){
        InOrder(ptr.sub[0]);
        for(int i=1;i <= ptr.num;++i){
            System.out.print(ptr.data[i].key+" ");
            InOrder(ptr.sub[i]);
        }
    }
}
public void InOrder(){
    if(root == null) return;
    InOrder(root);
}

你可能感兴趣的:(Java数据结构)