B树的性质
1.每个结点x有下面属性:
x.n,当前存储在结点x中的关键字个数。
x.n个关键字按非降序排列。
x.leaf,一个布尔值,如果x是叶结点,则为TRUE;如果x为内部结点,则为FALSE。
2.每个内部结点x有x.n+1个指向其孩子的指针,叶结点是没有孩子的。
3.和二叉搜索树和红黑树一样,也是左小右大。
4.每个叶结点具有相同的深度,即树的高度h。
5.每个结点所包含的关键字个数有上界和下界。用整数t(B树的最小度数)来表示这些界。除了根结点以外的每个结点必须至少有t-1个关键字。因也就意味着除了根结点以外的每个内部结点至少有t个孩子。如果树非空,根结点至少有一个关键字;每个结点至多有2t-1个关键字。因此,一个内部结点至多有2t个孩子。
B树上的基本操作
搜索B树
B-TREE-SEARCH(x,k)
i=1
while i<=x.n and k>x.key(i)
i=i+1
if i<=x.n and k==x.key(i)
return(x.i)
else if x.leaf
return NIL //x已经是叶子结点,但还没找到,说明不存在
else DISK-READ(x,c(i))
return B-TREE-SEARCH(X.(i),k) 递归到子树去寻找
向B树中插入一个关键字
分裂B树中的结点
1.如果树的内部结点x是非满的,也就是x.n<2t-1。而x.c(i)(也就是某个孩子结点)是满节点,意味着x.c(i).n=2t-1。该过程就是将这个满节点分裂成两个,然后将中间元素调整到x中。
2.如果是一个满根结点,要想分裂的话,先创建一个新的结点使之成为根结点,然后再按照1的步骤进行。
B-TREE-SPLIT-CHILD(x,i)
z=ALLOCATE-NODE() //分配一个新节点
y=x.c(i) //y是x的孩子,也是待分裂的结点
z.leaf=y.leaf //若y为叶子结点,则z也是,若y不是叶子结点,z也不是
z.n=t-1 //新节点z的关键字个数
for j=1 to t-1
z.key(i)=y.key(j+t) //z的关键字为y的关键字的后t-1个,也就是y结点中下标为j+1至2t-1的关键字
if not y.leaf
for j=1 to t
z.c(j)=y.c(j+t) //若y不是叶子结点,则将y的从t+1开始的孩子结点变成z的孩子结点
y.n=t-1
for j=x.n+1 downto i+1
x.c(j+1)=x.c(j) //把x的孩子从i+1开始集体后移一位
x.c(j+1)=z //将z赋予x的孩子结点
for j=x.n downto i
x.key(j+1)=x.key(i) //把x的关键字从i开始集体后移一位
x.key(i)=y.key(i) //把y的中间关键字插到x中
x.n=x.n+1 //x的关键字个数加一
DISK-WRITE(y)
DISK-WRITE(z)
DISK-WRITE(x)
向一棵B树中插入关键字
向B树中插入关键字F,这就是属于要分裂结点的情况,也是最复杂的情况
B-TREE-INSERT(T,k)
r=T.root
if r.n==2t-1 //如果根结点满了,进行特殊处理
s=ALLOCATE-NODE() //分配一个新的结点作为根,所以说对根进行分裂是增加B树高度的唯一途径
T.root=s
s.leaf=FALSE
s.n=0
s.c(1)=r //以r为根的子树是第一个孩子
B-TREE-SPLIT-CHILD(s,1) //利用分裂结点的情况处理
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 //如果x是叶子结点,则在x结点中找寻要插入的位置
while i>=1 and k<x.key(i) //找比新插入的关键字大的关键字
x.key(i+1)=x.key(i)
i=i-1
x.key(i+1)=k
x.n=x.n+1
DISK-WRITE(x)
else //如果x不是叶子结点,从合适的孩子结点里面去找
while i>=1 and k<x.key(i)
i=i-1
i=i+1 //合适的孩子结点下标
DISK-WRITE(x.c(i))
if x.c(i).n==2t-1 //如果孩子结点是满节点,进行分裂处理
B-TREE-SPLIT-CHILD(x,i)
if k>x.key(i)
i=i+1 //如果做了分裂处理,这是新的孩子结点下标
B-TREE-INSERT-NONFULL(x.c(i),k) //递归处理