B树

来源:互联网 发布:巅峰网络传奇教程 编辑:程序博客网 时间:2024/06/18 17:45
 

总结:本章介绍了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的一个关键字移至新合并的节点。