B树是为磁盘或其它直接存取的辅助设备而设计的一种平衡二叉树。一颗有n个节点的B树和红黑树一样高度为O(lgn)。但其实B树的高度远小于红黑树。
B树在算法导论上是这样定义的:
一棵B树T是具有以下性质的有根树(根为T.root):
1.每个结点x有下面性质:
a.x.n,当前存储在结点x中的关键字个数
b.x.n个关键字本身,以非降序存放
c.x.leaf,一个布尔值,如果x是叶节点则为TRUE,否则为FALSE
2.每个内部结点还包含x.n+1个指向其孩子的指针x.c1,x.c2,…叶节点没有孩子,所以它们的ci没有定义
3.关键字x.keyi对存储在各个子树中的关键字范围加以分割
4.每个叶节点具有相同的深度,即树的高度h
5.每个结点所包含的关键字个数有上界和下界。用一个被称为B树的最小度数的固定整数t>=2来表示这些界:
a.除了根节点以外的每个节点必须至少有t-1个关键字。因此,除了根节点以外的每个内部结点至少有t个孩子。如果树非空,根结点至少有一个关键字
b.每个结点至多包含2t-1个关键字。因此,一个内部结点至多有2t个孩子
B-TREE-SEARCH(x,k)
i=1
while i<=x.n and k>x.keyi
i=i+1
if i<=x.n and k == x.keyi
return (x,i)
elseif x.leaf
return nil
else DISK_READ(x,ci)
return B-TREE-SEARCH(x.ci,k)
B-TREE-CREATE(T)
x = ALLOCATE-NODE() //创建一个结点
x.leaf = TRUE
x.n = 0
DISK-WRITE(x)
T.root = x
B-TREE-SPLIT-CHILD(x,i) //此时x是非满结点的,x的i下标为满结点
z = ALLOCATE-NODE()
y = x.ci;
z.leaf = y.leaf
z.n = t-1
for j=1 to t-1 //调整z的关键字
z.keyj = y.keyj+t
if not y.leaf //调整z的孩子
for j=1 to t
z.cj = y.cj+t
y.n = t-1
for j=x.n+1 downto i+1 //调整x的孩子位置
x.cj+1 = x.ci
x.ci+1 = z
for j=x.n downto i
x.keyj+1 = x.keyj
x.keyi = y.keyt
x.n +=1
DISK-WRITE(y)
DISK-WRITE(z)
DISK-WRITE(x)
B-TREE-INSERT(T,k)
r=T.root
if r.n==2t-1 //处理根结点为满的情况,对根结点分裂是增加B树高度的唯一途径
s=ALLOCATE-NODE()
T.root=s
s.leaf = FALSE
s.n=0
s.c1=r
B-TREE-SPLIT-CHILD(s,1);//1代表是s的第一个孩子
B-TREE-INSERT-NONFULL(s,k)
else B-TREE-INSERT-NONFULL(r,k)
B-TREE-INSERT-NONFULL(x,k)
i=x.n
if x.leaf
while i>=1 and k<x.keyi
x.keyi+1=x.keyi
i=i-1
x.keyi+1=k
x.n=x.n+1
DISK-WRITE(x)
else while i>=1 and k <x.keyi //x不是叶节点,决定向哪个子结点递归下降
i=i-1
i=i+1
DISK-READ(x.ci)
if x.ci.n==2t-1
B-TREE-SPLIT-CHILD(x,i)
if k>x.keyi //这两行决定向哪个孩子下降正确
i=i+1
B-TREE-INSERT-NONFULL(x.ci,k)
B+Tree是B树的变种,也是MySql索引的实现方式有以下不同点:
每个节点的指针上限为2d而不是2d+1。
内节点不存储data,只存储key;叶子节点不存储指针。
还有的B+树是带顺序指针的,方便顺序访问。
首先,为什么MySql使用B树作为索引的数据结构?
索引文件由于较大,需要存储在磁盘中。而读取磁盘文件的时候,操作系统会一次读取相邻的一段数据,作为预读。预读时,读取的单位是页,而我们存储时,将一个节点存成一页。
B树中一次检索最多需要h-1次IO操作,复杂度渐进复杂度为O(h)=O(logdN)。d是很大的,所以效率是很高的。而红黑树的h较大,在逻辑上近的结点可能距离很远。所以效率很低。