B-树
package org.riley.tree; import java.util.Random; /** * 保存int的集合 集合中的元素存放在B-树中 * * @author dou * */ public class IntBalancedSet implements Cloneable { private static final int MINIMUM = 2;// 非根节点中最少存放的元素个数 private static final int MAXIMUM = 2 * MINIMUM;// 节点中最多存放的元素个数 int dataCount;// 存放节点中元素的个数 int[] data; int childCount;// 存放节点中子树的个数 IntBalancedSet[] subset; /** * 为数组提供额外的空间,方便对数组进行删除和添加操作 */ public IntBalancedSet() { data = new int[MAXIMUM + 1]; subset = new IntBalancedSet[MAXIMUM + 2]; } /** * 在集合中添加新元素 * * @param element */ public void add(int element) { looseAdd(element); /* * 如果根节点比MAXIMUM大时,将根节点复制给一个新节点.并清空根节点.将新节点作为个根节点的 子树节点.再对此子节点进行拆分. * B-数只在根节点处才能增加高度 */ if (dataCount > MAXIMUM) { IntBalancedSet cpy = copyData(); data = new int[MAXIMUM + 1]; dataCount = 0; childCount = dataCount + 1; subset = new IntBalancedSet[MAXIMUM + 2]; subset[0] = cpy; fixExcess(0); } } /** * B-树是有效的的前提下,可以调用此方法. 如果elememt在集合中存在.那么集合不变.否则,该元素添加到集合中.并且集合的根节点元素 * 数目允许比MAXIMUM大1 * * @param element */ private void looseAdd(int element) { int i = firstGE(element); if (i == dataCount) { if (childCount == 0) { insertEle(element, i); } else { subset[i].looseAdd(element); fixExcess(i); } } else { if (data[i] == element) { return; } else { if (childCount == 0) { insertEle(element, i); } else { subset[i].looseAdd(element); fixExcess(i); } } } } /** * 将element插入到locale位置.其后的元素往后移 * * @param element * @param locale */ private void insertEle(int element, int locale) { for (int i = dataCount; i > locale; i--) { data[i] = data[i - 1]; } data[locale] = element; dataCount++; // System.out.println(dataCount+" "+childCount); } /** * 将subset[i]拆分为两个元素个数为MINIMUM,子树数目为MINIMUM+1的两个节点. 分别存放在set1,和set2 * * @param set1 * @param set2 * @param i */ private void divoteSub(IntBalancedSet set1, IntBalancedSet set2, int i) { System.arraycopy(subset[i].data, 0, set1.data, 0, MINIMUM); System.arraycopy(subset[i].subset, 0, set1.subset, 0, MINIMUM + 1); System.arraycopy(subset[i].data, MINIMUM + 1, set2.data, 0, MINIMUM); System.arraycopy(subset[i].subset, MINIMUM + 1, set2.subset, 0, MINIMUM + 1); set1.dataCount = MINIMUM; set1.childCount = subset[i].childCount / 2; set2.dataCount = MINIMUM; set2.childCount = subset[i].childCount / 2; } /** * 调用此方法必须保证除了subset[i]有MAXIMUM+1个元素外,整棵B-树仍然有效的 * 将subset[i]拆分为两个元素个数为MINIMUM的节点.在将节点插入到根节点的i和i+1位置 * * @param i */ private void fixExcess(int i) { if (subset[i].dataCount > MAXIMUM) { IntBalancedSet set1 = new IntBalancedSet(); IntBalancedSet set2 = new IntBalancedSet(); insertEle(subset[i].data[MINIMUM], i);// 在父节点插入该中间元素. divoteSub(set1, set2, i); subset[i] = set1; for (int j = childCount; j > i + 1; j--) { subset[j] = subset[j - 1]; } subset[i + 1] = set2; childCount++; // System.out.println(data[0]); } } /** * 返回一个新的引用中的data和subset指向当前节点的data和subset; * * @return */ private IntBalancedSet copyData() { IntBalancedSet copy; copy = new IntBalancedSet(); copy.data = data; copy.dataCount = dataCount; copy.childCount = childCount; copy.subset = subset; return copy; } @Override public Object clone() { IntBalancedSet copy; copy = new IntBalancedSet(); copy.data = data.clone(); copy.dataCount = dataCount; copy.childCount = childCount; copy.subset = subset.clone(); return copy; } /** * 在B-树中查找target. * * @param target * @return */ public boolean contains(int target) { int i = firstGE(target); // System.out.println("i:"+i); if (i == dataCount) { if (childCount == 0) return false; else return subset[i].contains(target); } else { if (target == data[i]) { return true; } else { if (childCount == 0) return false; else return subset[i].contains(target); } } } /** * 返回根节点中第一个大于或等于target的元素的位置 如果没有这样的元素,则返回dataCount * * @param target * @return */ private int firstGE(int target) { int i = 0; for (; i < dataCount; i++) { if (data[i] >= target) return i; } return i; } /** * 在集合中删除元素 * * @param target * @return */ public boolean remove(int target) { boolean answer = looseRemove(target); /** * 如果根节点没有元素,并且仅有一个子节点.那么删除子节点 */ if ((dataCount == 0) && (childCount == 1)) { dataCount = subset[0].dataCount; childCount = subset[0].childCount; data = subset[0].data; subset = subset[0].subset; } return answer; } /** * 如果target在集合中,将他删除并返回true.否则返回false 执行该方法后根节点的元素数目可能比MINIMUM小1 * * @param target * @return */ private boolean looseRemove(int target) { int i = firstGE(target); if (childCount == 0) { if (i != dataCount && data[i] == target) { // 在data[i]中找到元素.并且没有子节点 cover(i); return true; } else { return false; } } else { if (i != dataCount && data[i] == target) { /** * 在data[i]中找到元素.但有子节点.将子节点中的最大元素覆盖i.既在所有比 data[i]小的元素集合中的最大值. * subset[i]的元素数目能比最小值小1 */ data[i] = subset[i].removeBiggest(); if (subset[i].dataCount < MINIMUM) fixShortage(i); return true; } else { /** * 在data[i]中找不到元素.但有子节点.递归调用子节点的该方法. */ boolean answer = subset[i].looseRemove(target); if (subset[i].dataCount < MINIMUM) fixShortage(i); return answer; } } } /** * 当subset[i]只有MINIMUM-1个元素时调用此方法 该方法使根节点的元素个数比MINIMU小1 * * @param i */ private void fixShortage(int i) { if (i != 0 && subset[i - 1].dataCount > MINIMUM) { /** * 如果subset[i-1]的元素数目比MINIMUM大.那么将data[i-1]的值插入到subset[i] * 的data[0]位置.再将subset[i-1]的最后个元素转移到data[i-1]的位置.如果subset[i-1] * 有子树那么将subset[i-1]的最大子树添加到subset[i]的subset[0]位置. */ subset[i].insertEle(data[i - 1], 0); data[i - 1] = subset[i - 1].cover(subset[i - 1].dataCount - 1); if (subset[i - 1].childCount != 0) { subset[i].addSubset(subset[i - 1].coverSub(subset[i - 1].childCount - 1), 0); } return; } if (i != 0 && subset[i - 1].dataCount == MINIMUM) { subset[i - 1].insertEle(data[i - 1], subset[i - 1].dataCount); cover(i - 1); combineSub(subset[i - 1], subset[i]); coverSub(i); return; } if (i < dataCount && subset[i + 1].dataCount > MINIMUM) { subset[i].insertEle(data[i], subset[i].dataCount); data[i] = subset[i + 1].cover(0); if (subset[i + 1].childCount != 0) { subset[i].addSubset(subset[i + 1].coverSub(0), subset[i].childCount); } return; } if (i < dataCount && subset[i + 1].dataCount == MINIMUM) { subset[i + 1].insertEle(data[i], 0); cover(i); combineSub(subset[i], subset[i + 1]); coverSub(i + 1); return; } } private int removeBiggest() { if (childCount == 0) { return data[--dataCount]; } else { int answer = subset[childCount - 1].removeBiggest(); if (subset[childCount - 1].dataCount < MINIMUM) { fixShortage(childCount - 1); } return answer; } } /** * 将set2的元素和孩子节点添加到set1的后面;保证set1+set2的元素和不大于MAXIMUM * * @param set1 * @param set2 */ private void combineSub(IntBalancedSet set1, IntBalancedSet set2) { for (int i = 0; i < set2.dataCount; i++) { // System.out.println(" combineSub "+set1.dataCount+" "+set2.dataCount); set1.data[set1.dataCount++] = set2.data[i]; } for (int i = 0; i < set2.childCount; i++) { set1.subset[set1.childCount++] = set2.subset[i]; } } /** * 删除节点中i位置的子节点.其后的子节点往前移动一个位置. * * @param i */ private IntBalancedSet coverSub(int i) { IntBalancedSet answer = subset[i]; for (int j = i; j < childCount; j++) { subset[j] = subset[j + 1]; } childCount--; return answer; } /** * 删除节点中i位置的元素.其后的元素往前移动一个位置. * * @param i */ private int cover(int i) { int answer = data[i]; for (int j = i; j < dataCount; j++) { data[j] = data[j + 1]; } dataCount--; return answer; } // private void tranferEle(int i,int flag){ // int dl=i+(flag==-1?0:flag);//datalocation // subset[i].insertEle(data[dl], 0); // data[dl]=subset[i+flag].data]; // if(subset[i-1].childCount!=0){ // subset[i-1].addSubset(subset[subset[i-1].childCount--],0); // } // } /** * 调用该方法的节点必须保证其子节点的数目比B-树要求的少一 调用后将sub添加到l位置.原本l后的节点往后移一个位置 * * @param sub * @param l */ private void addSubset(IntBalancedSet sub, int l) { for (int i = childCount; i > l; i--) { subset[i] = subset[i - 1]; } subset[l] = sub; childCount++; } // private int removeLastest(){ // // } public void print(int indent) { final int EXTRA_INDENTATION = 4; int i; int space; for (space = 0; space < indent; space++) { System.out.print(" "); } for (i = 0; i < dataCount; i++) { System.out.print(data[i] + ","); } System.out.print("DC:" + dataCount + " CC:" + childCount); System.out.println(); for (i = childCount - 1; i >= 0; i--) { subset[i].print(indent + EXTRA_INDENTATION); } } public static void main(String[] args) { IntBalancedSet set = new IntBalancedSet(); Random rg = new Random(); for (int i = 0; i < 2000; i++) { int num = rg.nextInt(10000); System.out.println("Adding " + num); set.add(num); // System.out.println("tree so far:"); // set.print(4); } System.out.println("final version of tree"); set.print(4); int num = rg.nextInt(10000); if (set.contains(num)) System.out.println("found it!"); else System.out.println("all that time, and nothing to show for it!"); } }