6.7 B-树★4◎3
1.B-树的定义
B-树是一种平衡的多路查找树,它在文件系统中很有用。
定义:一棵m阶的B-树,或者为空树,或为满足下列特性的m叉树:
(1)树中每个结点至多有m棵子树。
(2)若根结点不是叶子结点,则至少有两棵子树。
(3)除根结点之外的所有非终端结点至少有 棵子树。
(4)所有的非终端结点中包含以下信息数据:其中:为关键码,且为指向子树根结点的指针(i=0,1,…,n),且指针所指子树中所有结点的关键码均小于所指子树中所有结点的关键码均大于为关键码的个数。
(5)所有的叶子结点都出现在同一层次上,并且不带信息(可以看做是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。
如图6-8所示为一棵5阶的B-树,其深度为4。
2.B-树的查找
B-树的查找类似于二叉排序树的查找,所不同的是,B-树每个结点是多关键码的有序表。在到达某个结点时,先在有序表中查找,若找到,则查找成功;否则,到按照对应的指针信息指向的子树中去查找,当到达叶子结点时,则说明树中没有对应的关键码,查找失败,即在B-树上的查找过程是一个顺指针查找结点和在结点中查找关键码交叉进行的过程。比如,在图6-8中查找关键码为93的元素。首先,从t指向的根结点(a)开始,结点(a)中只有一个关键码,且93大于它,因此,按(a)结点指针域A1到结点(c)去查找,结点(c)有两个关键码,而93也都大于它们,应按(c)结点指针域A2到结点(i)去查找,在结点(i)中顺序比较关键码,找到关键码93(K3)。
【查找分析】
B-树的查找是由两个基本操作交叉进行的过程,即:
(1)在B-树上找结点。
(2)在结点中找关键码。
由于通常B-树是存储在外存上的,操作(1)就是通过指针在磁盘相对定位的,将结点信息读入内存之后,再对结点中的关键码有序表进行顺序查找或折半查找。因为在磁盘上读取结点信息比在内存中进行关键码查找耗时多,所以,在磁盘上读取结点信息的次数,即B-树的层次树是决定B-树查找效率的首要因素。
那么,对含有n个关键码的m阶B-树,最坏情况下达到多深呢?我们可按二叉平衡树进行类似分析。首先,讨论m阶B-树各层上的最少结点数。
由B-树定义:第一层至少有1个结点;第二层至少有2个结点;由于除根结点外的每个非终端结点至少有棵子树,则第3层至少有个结点;……依此类推,第k+1层至少有个结点。而k+1层的结点为叶子结点。若m阶B-树有n个关键码,则叶子结点即查找不成功的结点为n+1,由此有:
即
这就是说,在含有n个关键码的B-树上进行查找时,从根结点到关键码所在结点的路径上涉及的结点数不超过
3.B-树的插入和删除
(1)B-树的插入
在B-树上插入关键码,即在最底层的某个非终端结点中添加一个关键码。若该结点上关键码个数不超过m-1个,则可直接插入到该结点上;否则,该结点上关键码个数至少达到m个,因而使该结点的子树超过了m棵,这与B-树定义不符。所以要进行调整,即结点的“分裂”。方法为:关键码加入结点后,将结点中的关键码分成3部分,使得前后两部分关键码个数均大于等于而中间部分只有一个结点。前后两部分成为两个结点,中间的一个结点将其插入到父结点中。若插入父结点而使父结点中关键码个数超过m-1,则父结点继续分裂,直到插入某个父结点其关键码个数小于m。可见,B-树是从底向上生长的。
对关键码序列{20,54,69,84,71,30,78,25,93,41,7,76,51,66,68,53,3,79,35,12,15,6},建立5阶B-树的过程如图6-9所示。
①向空树中插入20,得图6-9(a)。
②插入54,69,84,得图6-9(b)。
③插入71,索引项达到5,要分裂成三部分:{20,54},{69}和{71,84},并将69上升到该结点的父结点中,得图6-9(c)。
④插入30,78,25,93,得图6-9(d)。
⑤插入41,又分裂得图6-9(e)。
⑥7直接插入。
⑦76插入,分裂得图6-9(f)。
⑧51,66直接插入,当插入68,需分裂,得图6-9(g)。
⑨53,3,79,35直接插入,12插入时,需分裂,但中间关键码12插入父结点时,又需要分裂,则54上升为新根。15,65直接插入得图6-9(h)。
(2)B-树的删除
B-树的删除操作分两种情况:
【删除最底层结点中的关键码】
① 若结点中关键码个数大于(m/2)-1,直接删去。
② 否则,若与该结点相邻的右(或左)兄弟结点中的关键码个数大于(m/2)-1,则将其兄弟结点中最小(或最大)的关键码上移到双亲结点,而将双亲结点中与小于(或大于)且紧靠该上移关键码的关键字下移到被删关键码的结点中。以图6-9(h)为例,删除结点中的76后得图6-10。
③ 若被删关键码结点及其相邻的兄弟结点的关键码个数都等于(m/2)-1,假设该结点有右(或左)兄弟,且其地址由父结点中的Ai所指,则删除关键码后,余项与父结点中的Ki一起合并到Ai所指的结点中。此时如果父结点的关键码小于(m/2)-1,则转②处理。如删去图6-9(h)中的7,得图6-11。
【删除为非底层结点中的关键码】
若所删除关键码非底层结点中的Ki,则可以用指针Ai所指子树中的最小关键码X替代Ki,然后,再删除关键码X,直到这个X在最底层结点上,即转为(1)的情形。