B-Tree(Balance Tree)的Java实现

来源:互联网 发布:seo在哪里可以自学 编辑:程序博客网 时间:2024/06/05 01:53

具体算法思想及应用介绍的文章网上有很多,这篇推荐给大家http://blog.csdn.net/hbhhww/article/details/8206846。

我就直接上自己的Java代码,之前写的时候也参考了一些其它语言的实现 。

/** * <code>B-Tree</code> : <p /> * 假设B树的度为 m(m>=2),则B树满足如下要求:(参考算法导论) * <br /> * (1)每个非根节点至少包含m-1个关键字,m个指向子节点的指针;至多包含2m-1个关键字,2m个指向子女的指针(叶子节点的子女为空) * <br /> * (2)节点的所有key按非降序存放,假设节点的关键字分别为K[1], K[2] … K[n], 指向子女的指针分别为P[1], P[2]…P[n+1], * 其中n为节点关键字的个数。则有:P[1] <= K[1] <= P[2] <= K[2] …..<= K[n] <= P[n+1] // 这里P[n]也指其指向的关键字 * <br /> * (3)若根节点非空,则根节点至少包含两个子女; * <br /> * (4)所有的叶子节点都在同一层 * <p/> *  *  * 查询:<br /> * 从<code>root</code>出发,对每个节点,找到大于或等于target关键字中最小的K[i] *  <br /> * (1)如果K[i]与target相等,则查找成功,返回一个标识true,和target下标i *  <br /> * (2)否则查找失败, 返回一个标识false,和一个下标指向其子节点位置,递归search新的子节点,如果是叶节点则表示不存在该关键字 *  * <p/> * 插入: * <br /> * B树的插入需要沿着搜索的路径从<code>root</code>一直到叶节点,根据B树的规则,每个节点的关键字个数在[m-1, 2m-1]之间, * 当target要加入到某个叶子时,如果该叶子节点已经有2m-1个关键字,则再加入target就违反了B树的定义, * 这时就需要对该叶子节点进行分裂,将叶子以中间节点为界,分成两个包含m-1个关键字的子节点, * 同时把中间节点提升到该叶子的父节点中,如果这样使得父节点的关键字个数超过2m-1, * 则要继续向上分裂,直到根节点,根节点的分裂会使得树加高一层 * <br /> * 关键:在下降的过程中,一旦遇到已满的节点(关键字个数为2m-1),就就对该节点进行分裂,这样就保证在叶子节点需要分裂时, * 其父节点一定是非满的,从而不需要再向上回溯 *  * <p/> * 删除: * <br /> * B树的删除同样需要沿着搜索的路径从<code>root</code>一直到叶节点 * 1,根节点只有一个实例,且起子节点都只包含<code>minEntrySize</code>个实例时,树降高(树降高的唯一情形) * <br /> * 2,如果在内部节点node中找到关键字key下标i,观察该节点的第i与i+i个子节点,在它俩中找到实例数大于<code>minEntrySize</code>的 *    记为tmpNode,中转tmpKey到当前node,在tmpNode中递归删除tmpKey,如果上述两个子节点实例数均不大于<code>minEntrySize</code> *    则借当前key合并节点 * <br />   * 3,与插入时避免回溯的思想一样,保证(2)中合并节点时,借走父节点的 一个实例,而不须继续向上回溯 */public class BTree<K extends Comparable<K>, V> {private static final int M = 4;private int minEntrySize;private int maxEntrySize;private Node root;private int size;private int depth;public BTree() {this(M);}public BTree(int m) {this.minEntrySize = m - 1;this.maxEntrySize = (m * 2) - 1;this.root = new Node(true);}/**  * @return 树中不存在当前key时返回null, 否则返回上次key保存的值  */public V put(K key, V value) {if (root.isFull()) {// 创建新rootNode newRoot = new Node(false);newRoot.children.add(root);splitFullNode(root, newRoot, 0);root = newRoot;depth++;}return insert(root, key, value);}public V get(K key) {return serach(root, key);}private V serach(Node current, K key) {SerachResult sr = current.serach(key);if (sr.serached) {return current.entrys.get(sr.wudindex).value;} else {if (!current.isLeaf) {return serach(current.children.get(sr.wudindex), key);}//递归到叶节点中仍没有查询到return null;}}public int size() {return size;}public boolean isEmpty() {return size == 0;}public int depth() {return depth;}private V insert(Node current, K key, V value) {if (current.isFull()) {throw new UnsupportedOperationException("只能为非满节点插入实例...");}SerachResult sr = current.serach(key);if (sr.serached) {V oldValue = current.entrys.get(sr.wudindex).value;current.entrys.get(sr.wudindex).value = value;return oldValue;}if (!current.isLeaf) {Node wudNode = current.children.get(sr.wudindex);if (wudNode.isFull()) {// 回溯过程中每遇到一个满实例节点就立刻分裂                // 保证在叶节点需要分裂时,其父节点一定是非满的,从而不需要再向上回溯splitFullNode(wudNode, current, sr.wudindex);if (key.compareTo(current.entrys.get(sr.wudindex).key) > 0) {//需要插入到分裂出的兄弟节点中wudNode = current.children.get(sr.wudindex + 1);}}return insert(wudNode, key, value);} else {//所有节点都在叶节点加入current.entrys.add(sr.wudindex, new Entry(key, value));size++;}return null;}/**  * 将一个满实例节点分割, 中间实例提取至父节点中, 分隔出的部分((m - 1)个)实例放入新生成的兄弟节点中  * @param parent 父节点  * @param child 满实例节点  * @param i 满实例节点在父节点中的位置(提取出中间实例应在父节点中插入的位置)  */private void splitFullNode(Node fullNode, Node parent, int index) {if (!fullNode.isFull()) {throw new UnsupportedOperationException("不能对非满节点经行分裂!");}int middleIndex = (fullNode.entrys.size() - 1) / 2;Entry middleEntry = fullNode.entrys.get(middleIndex);//此处已保证parent加入新节点后仍满足B-tree特性parent.entrys.add(index, middleEntry);Node sibling = new Node(fullNode.isLeaf);//采用向右分割for (int i = maxEntrySize - 1; i > middleIndex; i--) {sibling.entrys.add(0, fullNode.entrys.get(i));fullNode.entrys.remove(i);}fullNode.entrys.remove(middleIndex);parent.children.add(index + 1, sibling);//如果分割的不是叶节点, 则fullNode的后半部分(m个)子节点将成为sibling的子节点if (!fullNode.isLeaf) {int cs = fullNode.children.size();for (int i = cs - 1; i >= (cs / 2); i--) {sibling.children.add(0, fullNode.children.get(i));fullNode.children.remove(i);}}}private class Node {List<Entry> entrys;List<Node> children;boolean isLeaf;Node(boolean isLeaf) {this.entrys = new ArrayList<Entry>();this.children = new ArrayList<Node>();this.isLeaf = isLeaf; }/**  * 在当前节点中查询  * @param key 搜索关键字  * @return {@link SerachResult}  */SerachResult serach(K key) {int left = 0;int right = entrys.size() - 1;int middle = 0;while (left <= right) {middle = (left + right) / 2;K ck = entrys.get(middle).key;if (key.compareTo(ck) < 0) {right = middle - 1;} else if(key.compareTo(ck) > 0) {left = middle + 1;} else {break;}}boolean flag = true;if (left > right) {//未找到flag = false;}return new SerachResult(flag, left);}boolean isFull() {return entrys.size() == maxEntrySize;}}private class Entry {K key;V value;Entry(K key, V value) {this.key = key;this.value = value;}@Overridepublic String toString() {return "{key:" + key + ", value:" + value + "}";}}/** * 在节点中单次查询结果 */private class SerachResult {/** * 是否查询到 */boolean serached;/** * 查到时为在node实例集中的位置 * 未查到时表示在node子节点集对应节点中 */int wudindex;SerachResult(boolean serached, int wudindex) {this.serached = serached;this.wudindex = wudindex;}}}
我的实现里主要加入了泛型,提供了put(key, value); get(key); 两个操作。 大体上看就是一个HashMap的功能。

删除节点那部分的代码我这里没贴,因为实在太复杂也太长,我的代码也未必能让大家看的明白, 如果有兴趣的,自己动手实现一遍更好。 


0 0
原创粉丝点击