赞
踩
B树就是个树,和二叉树的区别是,二叉树只能有两个子节点,所以二叉树的劣势在于深度过深了
二叉树有点分治法里的二分,而B树就有点彻底的分治法的意思了
B树的内节点(即非叶节点)上也是有值的,为关键字,关键字的值是要有顺序的(我的代码里默认升序,关键字的值只是一个整数,其实可以保存对象)
B树的关键字个数不是随意的
1)我们有一个度数用t来表示,t>=2
2)关键字的个数m,m>=t-1,m<=2t-1,根节点不受限制最小限制,如果非叶节点,子节点个数为关键字个数+1
3)当关键字的个数m=2t-1时,这就是个满节点
4)每个叶节点要有相同的深度
要满足上面这些要求,所以添加和删除节点时就不能随意
添加:
1)添加只能添加到叶节点上,如果添加之后,改节点的关键字个数未超过限制,则结束
2)如果关键字个数为2*t个,提取第t个数字,将节点分割成两个节点,第t个数插入父节点中为关键字,如果此时父节点也关键字数溢出,再进行分割,直至结束
删除:
1)如果删除的值不存在,则不进行删除
2)如果删除的值在内节点上,将最邻近改节点的叶节点关键字(即该关键字左侧节点的最右子节点的最右子节点的…最右关键字 或 右侧节点的最左子节点的最左子节点的…最左关键字,我之所以选择左侧节点左右关键字,是因为我关键字是按数组存储,删除最右比删除最左简单很多)赋值到该节点,删除该邻近关键字,此时操作和3)一致
3)如果删除之后的关键字数不小于t-1个,就结束否则执行4)
4)找兄弟节点,如果兄弟节点大于t-1个,问兄弟节点借一个关键字(不是直接借,兄弟节点的值给父节点中两人的夹位,去此处的关键字,给自己),如果兄弟节点为t-1个关键字,执行5)
5)合并自己与兄弟节点,把两者夹位数也拿下来,组成新的节点,此时父节点会少一个关键字,判断父节点的后续操作(循环3、4、5)
附:不知道别的是用数组还是链表,查找时候没有用更快的二分查找,想着数组长度也不长,就直接顺序查找
因为书中只讲了B树没有讲B+树,所以就没B+的代码实现,有机会的话之后再实现
B+树与B树的区别:
最大区别在于,B+树所有的数据存在叶节点上,其他没有本质区别
红黑树:https://blog.csdn.net/qq_33321609/article/details/86702550
感觉红黑树最难,旋转就转死我了(数个月前写的,我现在看着都看不懂了)
效率分析
B树
取t值为30时
插入100w条数据,耗时227ms
删除0-100w数值各一次,耗时89ms
取t值为2时
插入100w条数据,耗时709ms
删除0-100w数值各一次,耗时142ms
红黑树
插入100w条数据,耗时935ms
删除0-100w数值各一次,耗时220ms
红黑树和B树t取值为2时相近
而且B树在内存使用上更少
当然,这些比较都是我的代码,所以不具有权威
这些都比数组和链表快
数组慢在于插入和删除(查询可以使用二分查找),链表在查询时候会慢(无论插入还是删除,都要先查询在哪操作)
代码实现:
Tree类
import java.util.ArrayList; import java.util.List; public class Tree { int t = 0; int num = 0; int[] keys; Tree[] children; boolean haveCh; public Tree(int t) { this.t = t; keys = new int[2 * t]; children = new Tree[2 * t + 1]; } public Tree(int t, int[] keys, int sta, int end) { this(t); for (int i = sta; i <= end; i++) { this.keys[num++] = keys[i]; } } public Tree(int t, int[] keys, int sta, int end, Tree[] children) { this(t); haveCh = true; for (int i = sta; i <= end; i++) { this.children[num] = children[i]; this.keys[num++] = keys[i]; } this.children[num] = children[end + 1]; } public AddReNode add(int value) { if (!haveCh) { //如果没有子节点 int i = 0; for (; i < num; i++) { if (keys[i] >= value) { for (int j = num; j > i; j--) { keys[j] = keys[j - 1]; } break; } } keys[i] = value; num++; //如果关键字个数过多 if (num == 2 * t) { //把节点分成两个节点 AddReNode addReNode = new AddReNode(); addReNode.key = keys[t]; addReNode.trees[0] = new Tree(t, keys, 0, t - 1); addReNode.trees[1] = new Tree(t, keys, t + 1, 2 * t - 1); this.keys[0] = keys[t]; this.children[0] = addReNode.trees[0]; this.children[1] = addReNode.trees[1]; this.num = 1; this.haveCh = true; return addReNode; } else { return null; } } else { //如果有子节点 int i = 0; for (; i < num; i++) { if (keys[i] >= value) { break; } } //在子节点上添加 AddReNode addReNode = children[i].add(value); if (addReNode != null) { //如果子节点添加后分裂 for (int j = num; j > i; j--) { keys[j] = keys[j - 1]; children[j + 1] = children[j]; } keys[i] = addReNode.key; children[i] = addReNode.trees[0]; children[i + 1] = addReNode.trees[1]; num++; //分裂 if (num == 2 * t) { AddReNode nddReNode = new AddReNode(); nddReNode.key = keys[t]; nddReNode.trees[0] = new Tree(t, keys, 0, t - 1, children); nddReNode.trees[1] = new Tree(t, keys, t + 1, 2 * t - 1, children); this.keys[0] = keys[t]; this.children[0] = nddReNode.trees[0]; this.children[1] = nddReNode.trees[1]; this.num = 1; this.haveCh = true; return nddReNode; } } return null; } } //删除 public boolean delete(int value) { int all = num; int i = 0; for (; i < num; i++) { if (value < keys[i]) { if (haveCh) { boolean b = children[i].delete(value); if (b) { //找右兄弟 this.removeRight(i); } } break; } if (value == keys[i]) { if (!haveCh) { for (int j = i; j < num - 1; j++) { keys[j] = keys[j + 1]; } num--; break; } Tree leftRight = getRight(children[i]); keys[i] = leftRight.keys[leftRight.num - 1]; boolean b = children[i].removeRight(); if (b) { //确保有有兄弟,所以找由兄弟 this.removeRight(i); } break; } } //说明前面一直没有删,那就再去判断最右一个child if (i == all) { if (haveCh) { boolean b = children[num].delete(value); if (b) { //找最右节点的左兄弟 this.removeTopLeft(); } } } return num < t - 1; } //如果节点数不够,和右节点合并或者借 private void removeRight(int key) { if (children[key + 1].num == t - 1) { //不够,合并 children[key].num += 1 + t - 1; children[key].keys[t - 2] = keys[key]; for (int j = 0; j < t - 1; j++) { children[key].keys[t - 1 + j] = children[key + 1].keys[j]; children[key].children[t - 1 + j] = children[key + 1].children[j]; } children[key].children[2 * t - 2] = children[key + 1].children[t - 1]; //后面的要移过来 for (int j = key; j < num - 1; j++) { keys[j] = keys[j + 1]; children[j+1] = children[j + 2]; } num--; } else { //如果右侧的兄弟节点够,就借 children[key].num++; children[key].keys[children[key].num - 1] = keys[key]; children[key].children[children[key].num] = children[key + 1].children[0]; keys[key] = children[key + 1].keys[0]; children[key + 1].removeLeft(); } } //删除最右的关键字 private boolean removeRight() { if (haveCh) { boolean b = children[num].removeRight(); if (b) { //因为没有右兄弟,所以找左兄弟 this.removeTopLeft(); } } else { num--; } return num < t - 1; } //虽然叫TopLeft,其实是删除最右的节点,如果节点数不够,和其左节点合并 private void removeTopLeft() { if (children[num - 1].num == t - 1) { //如果兄弟节点恰好是最少值,就进行合并 children[num - 1].num += 1 + t - 2; children[num - 1].keys[t - 1] = keys[num - 1]; for (int i = 0; i < t - 2; i++) { children[num - 1].keys[t + i] = children[num].keys[i]; children[num - 1].children[t + i] = children[num].children[i]; } children[num - 1].children[2 * t - 2] = children[num].children[t - 2]; num--; } else { //问兄弟节点借一个 children[num - 1].num--; int newChN = children[num - 1].num; children[num].addLeft(keys[num - 1], children[num - 1].children[newChN + 1]); keys[num - 1] = children[num - 1].keys[newChN]; } } //问左兄弟借来的节点 private void addLeft(int key, Tree node) { num++; children[num] = children[num - 1]; for (int i = num - 1; i >= 1; i--) { keys[i] = keys[i - 1]; children[i] = children[i - 1]; } keys[0] = key; children[0] = node; } //把最左的节点借给左兄弟 private void removeLeft() { for (int i = 0; i < num - 1; i++) { keys[i] = keys[i + 1]; children[i] = children[i + 1]; } num--; children[num] = children[num + 1]; } //获取最右的(因为使用数组,从右侧删除,方便) private Tree getRight(Tree tree) { if (tree.haveCh) { return getRight(tree.children[tree.num]); } return tree; } //获取所有节点 public static List<Integer> show(Tree tree) { List<Integer> ans = new ArrayList<>(); if (tree == null) { return ans; } if (tree.haveCh) { for (int i = 0; i < tree.num; i++) { ans.addAll(show(tree.children[i])); ans.add(tree.keys[i]); } ans.addAll(show(tree.children[tree.num])); } else { for (int i = 0; i < tree.num; i++) { ans.add(tree.keys[i]); } } return ans; } //校验关键字的数目 public static void checkTree(Tree tree, boolean ifTop) { if (tree.num > 2 * tree.t - 1) { System.out.println("存在关键字过多"); } if (!ifTop) { if (tree.num < tree.t - 1) { System.out.println("存在关键字过少"); } } } //树的深度 public static int deep(Tree tree) { if (tree.haveCh) { return 1 + deep(tree.children[0]); } return 1; } class AddReNode { int key; Tree[] trees = new Tree[2]; } }
Test类
public class Test { public static void main(String[] args) { Tree tree = new Tree(30); Random random = new Random(); for (int i = 0; i < 1000000; i++) { int v = random.nextInt(1000000); tree.add(v); } System.out.println("插入结束"); List<Integer> ans = Tree.show(tree); //System.out.println(ans); System.out.println(Tree.deep(tree)); System.out.println(ans.size()); for (int i = 1; i < ans.size(); i++) { if (ans.get(i) < ans.get(i - 1)) { System.out.println("顺序有误"); } } Tree.checkTree(tree, true); System.out.println("开始删除"); //int len = ans.size(); for (int i = 0; i < 1000000; i++) { tree.delete(i); if (tree.num == 0) { tree = tree.children[0]; } /*ans = Tree.show(tree); int dl = len-ans.size(); if(dl>1){ System.out.println("删除个数超过1"); } len = ans.size(); for (int j = 1; j < ans.size(); j++) { if (ans.get(j) < ans.get(j - 1)) { System.out.println("顺序有误"); } }*/ } System.out.println(Tree.deep(tree)); ans = Tree.show(tree); System.out.println(ans.size()); for (int i = 1; i < ans.size(); i++) { if (ans.get(i) < ans.get(i - 1)) { System.out.println("顺序有误"); } } Tree.checkTree(tree, true); /*int deep = Tree.deep(tree); while(Tree.deep(tree)==deep){ for (int i = 0; i < 1000000; i++) { tree.delete(i); if (tree.num == 0) { tree = tree.children[0]; } } System.out.println(Tree.deep(tree)); ans = Tree.show(tree); System.out.println(ans.size()); } deep = Tree.deep(tree); while(Tree.deep(tree)==deep){ for (int i = 0; i < 1000000; i++) { tree.delete(i); if (tree.num == 0) { tree = tree.children[0]; } } System.out.println(Tree.deep(tree)); ans = Tree.show(tree); System.out.println(ans.size()); } deep = Tree.deep(tree); while(Tree.deep(tree)==deep){ for (int i = 0; i < 1000000; i++) { tree.delete(i); if (tree.num == 0) { tree = tree.children[0]; } } System.out.println(Tree.deep(tree)); ans = Tree.show(tree); System.out.println(ans.size()); } ans = Tree.show(tree); System.out.println(ans);*/ } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。