从B树中删除关键字

《算法导论》练习18.3-2 请写出B-TREE-DELETE的伪代码。

 

与插入情况相对称,除了根结点外(根结点个数不能少于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个关键字下移与yz的合并,形成一个结点y

 

B树中结点的合并:

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

1 n[y] ← 2t -1

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

3   do keyj[y] keyjt[z]

4 keyt[y] keyi[x]

5 if not leaf[y]

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

7        do cj[y] cjt[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)

1  r  root[T]
 2  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)

1  i  1
 2  if leaf[x]                                        Cases 1
 3     then while i <= n[x] and k > keyi[x]
 4            do i  i + 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 i  i + 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
2  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树中删除关键字》

 

    原文作者:B树
    原文地址: https://blog.csdn.net/swordmanwk/article/details/6549480
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞