在计算机科学中,B树(英语:B-tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。
首先,包括红黑树是将输入存入内存的一种内部查找树。
而B树是前面平衡树算法的扩展,它支持保存在磁盘或者网络上的符号表进行外部查找,这些文件可能比我们以前考虑的输入要大的多(难以存入内存)。
既然内容保存在磁盘中,那么自然会因为树的深度过大而造成磁盘I/O读写过于频繁(磁盘读写速率是有限制的),进而导致查询效率低下。
那么降低树的深度自然很重要了。因此,我们引入了B树,平衡多路查找树。
上图是一颗3阶B树
为了方便这里用了一个特殊的哨兵键,它小于其他所有键,用*表示。
一开始B树只含有一个根结点,而根结点在初始化时仅含有该哨兵键。
内部结点中的每个键都与一个结点相关联,以此结点为根的子树种,所有的键都大于等于与此结点关联的键,但小于其他所有键。
这些约定在很大程度上能够简化代码。
Java 代码实现
package tree;
/**
* Created by bruce_shan on 2018/7/8 17:08.
* Description : B-树 也作 B树 java 实现
*/
public class BTree, Value> {
private static final int M = 4; // B树的阶数
private Node root; // B-tree 的根节点
private int height; // B-tree 的高度
private int N; // B-tree 树中键值对的数目
// B-tree 节点类型
private static final class Node {
private int m; // number of children
private Entry[] children = new Entry[M]; // the array of children
// create a node with k children
private Node(int k) {
m = k;
}
}
// B-tree 节点中的元素类型
private static class Entry {
private Comparable key;
private Object val;
private Node next; // 指向节点中下一元素
public Entry(Comparable key, Object val, Node next) {
this.key = key;
this.val = val;
this.next = next;
}
}
/**
* 初始化空 B-tree树
*/
public BTree() {
root = new Node(0);
}
/**
* 判断 B-tree 是否是空树
*/
public boolean isEmpty() {
return size() == 0;
}
public int size() {
return N;
}
public int height() {
return height;
}
/**
* get操作
*/
public Value get(Key key) {
if (key == null) throw new NullPointerException("key must not be null");
return search(root, key, height);
}
/**
* put 操作
*/
public void put(Key key, Value val) {
if (key == null) throw new NullPointerException("key must not be null");
Node u = insert(root, key, val, height);
N++;
if (u == null) return;
// need to split root
Node t = new Node(2);
t.children[0] = new Entry(root.children[0].key, null, root);
t.children[1] = new Entry(u.children[0].key, null, u);
root = t;
height++;
}
// 搜索操作
private Value search(Node x, Key key, int ht) {
Entry[] children = x.children;
// 节点内数组操作 内部遍历
if (ht == 0) {
for (int j = 0; j < x.m; j++) {
if (equals(key, children[j].key)) return (Value) children[j].val;
}
}
// 外部定位
else {
for (int j = 0; j < x.m; j++) {
if (j+1 == x.m || less(key, children[j+1].key))
return search(children[j].next, key, ht-1);
}
}
return null;
}
// 插入操作
private Node insert(Node h, Key key, Value val, int ht) {
int j;
Entry t = new Entry(key, val, null);
// 节点内部数组操作
if (ht == 0) {
for (j = 0; j < h.m; j++) {
if (less(key, h.children[j].key)) break;
}
}
// 外部遍历
else {
for (j = 0; j < h.m; j++) {
if ((j+1 == h.m) || less(key, h.children[j+1].key)) {
Node u = insert(h.children[j++].next, key, val, ht-1);
if (u == null) return null;
t.key = u.children[0].key;
t.next = u;
break;
}
}
}
for (int i = h.m; i > j; i--)
h.children[i] = h.children[i-1];
h.children[j] = t;
h.m++;
if (h.m < M) return null;
else return split(h);
}
// 分裂节点成两半
private Node split(Node h) {
Node t = new Node(M/2);
h.m = M/2;
for (int j = 0; j < M/2; j++)
t.children[j] = h.children[M/2+j];
return t;
}
// 判断两个元素是否相等
private boolean equals(Comparable k1, Comparable k2) {
return k1.compareTo(k2) == 0;
}
// 判断两个元素的大小
private boolean less(Comparable k1, Comparable k2) {
return k1.compareTo(k2) < 0;
}
}