总结:本章介绍了B树,并介绍了B树中搜索,插入,删除,分裂的算法。
1. B树的定义
B树是与红黑树类似的一颗平衡查找树,但在降低磁盘I/O操作次数方面要更好一些。
B树的性质:
1) 每个节点x的域:
a) n[x],x中的关键字数,若x是B树中的内节点,则x有n[x]+1个子女。
b) n[x]个关键字本身,以非降序排列,key1[x]<=key2[x]<=…<=keyn[x][x]
c) leaf[x],布尔值,如果x是叶节点,则为TRUE,若为内节点,则为FALSE
2) 每个内节点x还包含n[x]+1个指向其子女的指针c1[x], c2[x], …, cn[x]+1[x]
3) 如果ki为存储在以ci[x]为根的子树中的关键字,则k1<=key1[x]<=k2<=key2[x]<=…<=keyn[x][x]<=keyn[x]+1
4) 每个叶节点具有相同的深度
5) B树的最小度数t
a) 每个非根的节点必须至少有t-1个关键字
b) 每个节点可包含至多2t-1个关键字。
在B树中,一个节点的大小通常相当于一个完整的磁盘页。
2. 搜索操作
调用B-TREE-SEARCH(root[T],k),查找关键字k。返回(y,i),其中,y为节点,i为下标,满足keyi[y]=k
分析:磁盘存取的次数O(h)=O(logtn)
CPU运行时间O(th)=O(t logtn)
伪代码
B-TREE-SEARCH(x,k)
i <- 1
while i<=n[x] and k>keyi[x]
do i <- i+1
if i<=n[x] and k= keyi[x]
then return (x,i)
if leaf[x]
then return NIL
else DISK-READ(ci[x])
return B-TREE-SEARCH(ci[x],k)
3. 创建空树
分析:磁盘存取的次数O(1)
CPU运行时间O(1)
伪代码
B-TREE-CREATE(T)
x <- ALLOCATE-NODE()
leaf[x] <- TRUE
n[x] <- 0
DISK-WRITE(x)
root[T] <- x
4. 分裂
分裂是树长高的唯一途径。
设x是个非满节点,y是个满节点,且ci[x]=y。则分裂操作指,对y按其中间关键字S进行分裂,S被提升到y的双亲节点x中。
分析:磁盘操作O(1)
CPU时间O(t)
伪代码
B-TREE-SPLIT-CHILD(x,i,y)
z <- ALLOCATE-NODE()
leaf[z] <- leaf[y]
n[z] <- t-1
for j <- 1 to t-1
do keyj[z] <- keyj+t[y]
if not leaf[y]
then for j <- 1 to t
do cj[z] <- cj+t[y]
n[y] <- t-1
for j <- n[x]+1 downto i+1
do cj+1[x] <- cj[x]
ci+1[x] <- z
for j <- n[x] downto i
do keyj+1[x] <- keyj[x]
keyi[x] <- keyt[y]
n[x] <- n[x]+1
DISK-WRITE(y)
DISK-WRITE(z)
DISK-WRITE(x)
5. 插入
B树的插入是将新关键码插入到一个已经存在的叶节点上,由于不能将关键字插入到满的叶节点上,因此需要引入分裂操作。
在插入操作中,当沿着树往下查找新关键字所属位置时,就分裂沿途遇到的每个满节点,因此,每当要分裂一个满节点时,就能确保它的双亲不是满的。
分析:磁盘存取操作O(h)
CPU运行时间O(th)
伪代码
B-TREE-INSERT(T,k)
r <- root[T]
if n[r]=2t-1
then s <- ALLOCATE-NODE()
n[s] <- 0
leaf[s] <- FALSE
root[T] <- s
c1[s] <- r
B-TREE-SPLIT-CHILD(s,1,r)
B-TREE-NONFULL(s,k)
else B-TREE-NONFULL(r,k)
伪代码
B-TREE-NONFULL(x,k)
i <- n[x]
if leaf[x]
then while i>=1 and k<keyi[x]
do keyi+1[x] <- keyi[x]
i <- i-1
keyi+1[x] <- k
n[x] <- n[x]+1
DISK-WRITE(x)
else while i>=1 and k<keyi[x]
do i <- i-1
i <- i+1
if n[ci[x]]=2t-1
then B-TREE-SPLIT-CHILD(x,i,ci[x])
if k>keyi[x]
then i <- i+1
B-TREE-INSERT-NONFULL(ci[x],k)
6. 删除
删除操作,书上没有给出伪代码,只给出了基本思路,设关键字为k,x为节点
1) 若k在x中,且x是叶节点,则从x中删除k
2) 若k在x中,且x是内节点,则
a) 若x中前于k的子节点y包含至少t个关键字,则找出k在以y为根的子树中的前驱k’。递归地删除k’,并在x中用k’取代k。
b) 若x中后于k的子节点z包含至少t个关键字,则找出k在以z为根的子树中的后继k’。递归地删除k’,并在x中用k’取代k。
c) 否则,将k和z所有关键字合并进y,然后,释放z并将k从y中递归删除。
3) 若k不在x中,则确定必包含k的正确的子树的根ci[x]。若ci[x]只有t-1个关键字,则执行a或b操作。然后,对合适的子节点递归删除k。
a) 若ci[x]只包含t-1个关键字,但它的相邻兄弟包含至少t个关键字,则将x中的某一个关键字降至ci[x],将ci[x]的相邻兄弟中的某一个关键字升至x,将该兄弟中合适的子女指针迁移到ci[x]中。
b) 若ci[x]与其所有相邻兄弟节点都包含t-1个关键字,则将ci[x]与一个兄弟合并,将x的一个关键字移至新合并的节点。