B树学习----查询 插入 删除

参考算法导论第三版算法

1.B树的定义spa

任何和关键字相联系的“卫星数据”将于关键字同样存放在同一个节点中。指针

一棵B树T是具备如下性质的有根树(根为T.root):递归

1.每一个节点x都有下面属性:ci

a. x.n, 当前存储在节点x中的关键字个数。搜索

b. x.n, n个关键字自己x.key1, x.key2, ..., x.keyz, ..., x.keyx.n, 以非降序存放,是的x.key1 <= x.key2 <= ... <= x.keyx.n.循环

c. x.leaf, 一个bool值,若是x是叶节点,则为TRUE;若是x为内部节点,则为FALSE.程序

2.每一个内部节点x还包含x.n+1个指向其孩子的指针x.c1, x.c2, ... , x.cx.n+1。叶节点没有孩子,因此它们的ci属性没有定义。im

3.关键字x.keyi对存储在各子树种的关键范围加以分割:若是ki为任意一个存储在以x.ci为根的子树中的关键字,那么error

    

4.每一个叶节点具备相同的深度,即树的高度h。

5.每一个节点所包含的关键字个数有上界和下界。用一个被称为最小度数(minmum degree)的固定整数 t>=2来表示这些界。

a.除了根节点之外的每一个节点必须至少有t-1个关键字。所以,除了根节点之外的每一个节点至少有t个孩子。若是树非空,根节点至少有一个关键字。

b.每一个节点至多可包含2t-1个关键字。所以,一个内部节点至多可有2t个孩子。当一个节点刚好有2t-1个关键字是,则称该节点是满的(full)。

t = 2时的B树是最简单的。每一个内部节点有2个、3个或4个孩子,即一棵2-3-4树。然而在实际中,t的值越大,B树的高度就越小。

B树的高度

B树上大部分的操做所需的磁盘存取次数与B树的高度是成正比的。如今来分析B树最坏状况下的高度。

定理18.1 若是 n >= 1,那么对任意一棵包含n个关键字、高度为h、最小读书t>=2的B树T来讲,有

                                        

搜索B树

  

建立一棵空的B树

     

向B树种插入一个关键字

将一个满的节点y(有2t-1个关键字)按其中间关键字(median key) y.keyt分裂成两个各含t-1个关键字的节点。中间节点被提高到y的父节点,以标识两棵树的划分点。可是若是y的父节点也是满的,就必须在插入新的关键字以前将其分裂,最终满节点的分裂会沿着树向上传播。

当沿着树往下查找新的关键字所属位置时,就分裂沿途遇到的每一个满节点(包括叶节点自己)。所以,每当要分裂一个满节点y是,就能确保它的父节点不是满的。

分裂B树中的节点

过程B-TREE-SPLIT-CHILD的输入是一个非满的内部节点x和一个使x.ci为x的满子节点的下标i。该过程把这个子节点分裂成两个,并调整x,使之包含更多的孩子。要分裂一个满的根,首先要让根成为一个新的空根节点的孩子,才能使用B-TREE-SPLIT-CHILD.树的高度所以增长1,分裂是树长高的惟一途径。



以沿树单程下行方式向B树插入关键字

在一棵高度为h的B树T中,以沿树单程下行方式插入一个关键字k的操做须要O(h)次磁盘存取。所须要的CPU时间为O(th) = O(tlogn)。过程B-TREE-SPLIT-CHILD来保证地柜始终不会降至一个满节点上。



辅助的递归过程B-TREE-NONFULL将关键字插入节点x,要求假定在调用该过程时x是非满的。操做B-TREE-INSERT和递归操做B-TREE-INSERT-NONFULL保证了这个假设成立。



从B树中删除关键字

B树上的删除操做与插入操做相似,只是略微复杂一下,由于能够从任意一个节点中删除一个关键字,而不只仅是叶节点,并且当从一个内部节点删除一个关键字是,还要从新安排这个节点的孩子。与插入操做同样,必须防止因删除操做二致使树的结构违反B树性质。就像必须保证一个节点不会由于插入而变得太大同样,必须保证一个节点不会在删除期间变得过小(根节点除外)。

与插入状况相对称,除了根结点外(根结点个数不能少于1),B树的关键字数不能少于t-1个。对于简单删除状况,若是咱们定位到关键字处在某个结点中,若是这个结点中关键字个数刚好是t-1个,若是直接删除这个关键字,就会违反B树规则。

此时,须要考虑两种处理方案:

1)把这个结点与其相邻结点合并,合并时须要把父结点的一个关键字加进来,除非相邻的那个结点的关键字数也是t-1个,不然,合并后会超出2t-1的限制,一样违反B树规则。并且,由于从父结点拉下一个关键字,致使父结点的关键字数少1,若是原来父结点关键字数是t-1,那么父结点违反B树规则,这种状况下,必须进行回溯处理。(对于下图(a)初始树,删除结点Z就会出现这种状况)

2)从相邻结点借一个关键字过来,这种状况要求,相邻结点必须有多于t-1个关键字,借的过程当中,须要转经父结点,不然违反B树规则。

 

为了不回溯,要求咱们在从树根向下搜索关键字的过程当中,凡是遇到途经的结点,若是该结点的关键字数是t-1,则咱们须要想办法从其余地方搞个关键字过来,使得该结点的关键字数至少为t。

搞,也是从相邻结点搞,若是相邻结点有的话,固然,也要通过父结点进行周转。若是没有,就说明相邻结点的关键字个数也是t-1,这种状况,直接对该结点与其相邻结点进行合并,以知足要求。

 

B树的结点的合并基于以下状况调用:内结点x的第i个子结点y和第i+1个子结点z的关键字数都是t-1,此时须要把内结点x的第i个关键字下移与y和z的合并,造成一个结点y。

 

B树中结点的合并:

B-TREE-MERGE-CHILD(x, i, y,z)

n[y] ← 2t -1

2 for j ← t +1 to 2t -1

3   do keyj[y] ← keyj-t[z]

keyt[y] ← keyi[x]

5 if not leaf[y]

6  then for j ← t +1 to 2t -1

7        do cj[y] ← cj-t[z]

8 for j ← i +1 to n[x]

9  do cj[x] ← cj+1[x]

10 n[x] ← n[x] -1

11 FREE-NODE(z)

12 DISK-WRITE(y)

13 DISK-WRITE(z)

14 DISK-WRITE(x)

 

 

B树的删除:

B-TREE-DELETE(T,k)

rroot[T]
if n[r] = 1
 3    then DISK_READ(c1[r])
 4       DISK_READ(c2[r])
 5       y ←c1[r]
 6       z ←c2[r]
 7       if n[y] = n[z] = t-1                   ▹ Cases 2c or 3b
 8         then  B-TREE-MERGE-CHILD(r, 1, y, z) 
 9            root[T] ← y
 10           FREE-NODE(r)
 11           B-TREE-DELETE-NONONE(y, k)
12      else B-TREE-DELETE-NONONE (r, k)
13 else B-TREE-DELETE-NONONE (r, k)
 
考虑到根结点的特殊性,对根结点为1,而且两个子结点都是t-1的状况进行了特殊的处理:
先对两个子结点进行合并,而后把原来的根删除,把树根指向合并后的子结点y。
这样B树的高度就减小了1。这也是B树高度惟一会减小的状况。 

除了这种状况之外,就直接调用子过程B-TREE-DELETE-NONONE (x, k)。

 

B-TREE-DELETE-NONONE (x, k)

i ← 1
if leaf[x]                                       ▹ Cases 1
 3     then while i <= n[x] and k > keyi[x]
 4            do ii + 1
 5               if k = keyi[x]
 6                 then for j ← i+1 to n[x]
 7                        do keyj-1[x] ←keyj[x]
 8                      n[x] ← n[x] - 1
 9                      DISK-WRITE(x)
 10              else error:”the key does not exist”
 11    else while i <= n[x] and k > keyi[x]
12           do ii + 1
 13              DISK-READ(ci[x])
 14              y ←ci[x]
 15              if i <= n[x]
 16                then DISK-READ(ci+1[x])
 17                     z ←ci+1[x]
 18              if k = keyi[x]                          ▹ Cases 2
19                then if n[y] > t-1                   ▹ Cases 2a
 20                       then k′←B-TREE-SEARCH-PREDECESSOR(y)
 21                            B-TREE-DELETE-NONONE (y, k′)
 22                            keyi[x] ←k
 23                     else if n[z] > t-1               ▹ Cases 2b
 24                       then k′←B-TREE-SEARCH-SUCCESSOR (z)
 25                            B-TREE-DELETE-NONONE (z, k′)
 26                            keyi[x] ←k
 27                     else B-TREE-MERGE-CHILD(x, i, y, z)▹ Cases 2c
 28                          B-TREE-DELETE-NONONE (y, k)
 29              else                                   ▹ Cases 3
 30                if i >1
 31                  then DISK-READ(ci-1[x])
 32                       p ←ci-1[x]
 33                if n[y] = t-1 
 34                  then if i>1 and n[p] >t-1               ▹ Cases 3a
 35                         then B-TREE-SHIFT-TO-RIGHT-CHILD(x,i,p,y)
 36                       else if i <= n[x] and n[z] > t-1    ▹ Cases 3a
 37                         then B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z)
 38                       else if i>1                       ▹ Cases 3b
 39                         then B-TREE-MERGE-CHILD(x, i, p, y)  
 40                              y ← p
 41                       else B-TREE-MERGE-CHILD(x, i, y, z)▹ Cases 3b
 42                B-TREE-DELETE-NONONE (y, k)
 
查找前驱
B-TREE-SEARCH-PREDECESSOR(y)
1  x ← y
i ← n[x]
3  while not leaf[x]
4    do DISK_READ(ci+1[x])
5       x ←ci+1[x]
6       i ← n[x]
7  return keyi[x]
 
查找后继
B-TREE-SEARCH-SUCCESSOR (z)
1  x ← z
2  while not leaf[x]
3    do DISK_READ(c1[x])
4       x ←c1[x]
5  return key1[x]
 
转移到右边的子结点
B-TREE-SHIFT-TO-RIGHT-CHILD(x,i,y,z)
1 n[z] ← n[z] +1
2 j ← n[z]
3 while j > 1
4   do keyj[z] ←keyj-1[z]
5      j ← j -1
6 key1[z] ←keyi[x]
7 keyi[x] ←keyn[y][y]
8 if not leaf[z]
9   then j ← n[z]
10       while j > 0
11         do cj+1[z] ←cj[z]
12            j ← j -1
13       c1[z] ←cn[y]+1[y]
14 n[y] ← n[y] -1

15 DISK-WRITE(y)

16 DISK-WRITE(z)

17 DISK-WRITE(x)

转移到左边的子结点
B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z)
1 n[y] ← n[y] +1
2 keyn[y][y] ← keyi[x]
3 keyi[x] ←key1[z]
4 n[z] ← n[z] -1
5 j ← 1
6 while j <= n[z]
7   do keyj[z] ←keyj+1[z]
8      j ← j +1
9 if not leaf[z]
10  then cn[y]+1[y] ←c1[z]
11       j ← 1
12       while j <= n[z]+1
13         do cj[z] ←cj+1[z]
14            j ← j + 1

15 DISK-WRITE(y)

16 DISK-WRITE(z)

17 DISK-WRITE(x)

注意:每次递归调用前,程序都能保证包括关键字的子树根的关键字数至少为t(除了根结点外),

这是B-TREE-DELETE-NONONE子过程可以正确运行的关键,相似的,

能够用循环不变式证实B-TREE-DELETE-NONONE子过程的正确性。

 

B树的删除

 

B树删除续