B树 学习笔记2 - B树上的基本操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rsy56640/article/details/80139166

接上,算法导论 - 18章-2 B树上的基本操作

本节将给出 B-TREE-SEARCH、B-TREE-CREATE 和 B-TREE-INSERT 操作的细节。在这些过程中,我们采用两个约定:

1. B树的根结点始终在主存中,这样无需对根做 DISK-READ 操作;然而,当根结点被改变后,需要对根结点做一次 DISK-WRITE 操作。

2. 任何被当做参数的结点在被传递之前,都要对它们做一次 DISK-READ 操作。我们给出的过程都是“单程”算法,即它们从树的根开始向下,没有任何返回向上的过程。



搜索B树
根据结点的孩子做多路选择。即做 ( x . n + 1 ) 路选择


B-TREE-SEARCH 是 BST 搜索的直接推广,它的输入是一个某结点的指针,以及要在该子树中搜索的一个关键字 k 。顶层调用标准为 B-TREE-SEARCH( T . r o o t ,   k )。
如果 k 在 B树中,那么返回的是一个有序对( y ,   i ),其中 y . k e y i = k ;否则返回 NIL。

B-TREE-SEARCH( x ,   k )
这里写图片描述
说明:
第 1-3 行找出下标 i ,使得 k x . k e y i
第 4-5 行检查是否已经找到关键字。
第 6-7 行检查是叶结点,返回NIL。
第 8-9 行向下递归搜索。

总的CPU时间是 O ( t   l o g t n )


创建一棵空的 B树
先用 B-TREE-CREATE 来创建一个空的结点,然后调用 B-TREE-INSERT 来添加新的关键字。这些过程都要用到一个辅助过程 ALLOCATE-NODE,它在 O ( 1 ) 时间内为一个新结点分配一个磁盘页。

B-TREE-CREATE( T )
这里写图片描述
B-TREE-CREATE( T ) 需要 O ( 1 ) 次的磁盘操作和 O ( 1 ) 的CPU时间。


向 B树中插入一个关键字
将新关键字插入到一个已经存在的叶节点上。
若满,则分裂,将中间关键字 y . k e y t 提升到其父结点 x
为了避免分裂沿树向上传播,在向下查找新的关键字时,就分裂沿途遇到的每个满结点(包括叶结点本身)。
因此,每当要分裂一个满结点 y 时,就能确保它的父节点不是满的。


分裂 B树中的结点
过程 B-TREE-SPLIT-CHILD 的输入是一个非满的内部结点 x (假定在主存中)和一个使 x . c i (也假定在主存中)为 x 的满子结点的下标 i 。该过程把这个子结点分裂成两个,并调整 x ,使之包含多出来的孩子。
要分裂一个满的根,首先要让根称为一个新的空根结点的孩子,这样才能使用 B-TREE-SPLIT-CHILD 。树的高度因此增加1。
分裂根是树长高的唯一途径。
这里写图片描述
B-TREE-SPLIT-CHILD( x ,   i ) 分裂 x . c i
这里写图片描述
开始时,结点 y 2 t 个孩子( 2 t 1 个关键字),在分裂后减少至 t 个孩子( t 1 个关键字)。结点 z 拿走 y t 个最大的孩子(后半部分),并且 z 成为 x 的新孩子,它在 x 的孩子列中仅位于 y 之后。 y 的中间关键字上升到 x 中,成为分隔 y z 的关键字。
代码说明:
第 1-9 行创建结点 z , 并将 y t 1 个关键字以及相应的 t 的孩子给它。
第 10 行调整 y 的关键字个数。
第11-17 行将 z 插入为 x 的孩子,并提升 y 的中间关键字到 x 来分隔 y z ,然后调整 x 的关键字个数。
第 18-20 行写回所有被修改过的磁盘页面。
这里写图片描述
B-TREE-SPLIT-CHILD 占用的CPU时间为 Θ ( t ) ,并执行 O ( 1 ) 次磁盘操作。



以沿树单程向下方式向 B树插入关键字
B-TREE-INSERT( T ,   k )
以沿树单程下行方式插入一个关键字 k
需要 O ( h ) 次磁盘存取。所需CPU时间为 O ( t h ) = O ( t l o g t n )
过程 B-TREE-INSERT 利用 B-TREE-SPLIT-CHILD 来保证递归始终不会降至一个满结点上。
这里写图片描述
第 3-9 行处理了根结点 r 为满的情况:根结点分裂,一个新的结点 s (有两个孩子)成为根。(对根进行分裂是增加 B树高度的唯一途径)。
过程通过调用 B-TREE-INSERT-NONFULL 完成将关键字 k 插入以非满的根结点为根的树中。
B-TREE-INSERT-NONFULL 在需要时沿树向下递归,在必要时通过调用 B-TREE-SPLIT-CHILD 来保证任何时刻它所递归处理的结点都是非满的。
这里写图片描述
辅助的递归过程 B-TREE-INSERT-NONFULL 将关键字 k 插入结点 x ,要求假定在调用该过程时 x 是非满的。操作 B-TREE-INSERT 和 B-TREE-INSERT-NONFULL 保证这个假设成立。
这里写图片描述
第 2 行判断是否为叶结点。
第 3-8 行处理 x 是叶结点的情况,将关键字 k 插入 x
若是叶结点,从右端开始右移,并找到合适位置,并调整 x 的参数,最后写回磁盘。
若不是叶结点,则判断插入位置
第 9-11 行计算应插入的子结点的位置( k x . k e y i 则应插入到 c i + 1 )
第 13 行检查是否为满结点。
若是满结点,第 14 行调用分裂函数。
第 15-16 行判断分裂出来的两个结点选择哪一个。
(注:即使i++,也无需DISK-READ,因为刚刚分裂的两个结点都在主存中)
第 17 行递归地将 k 插入合适的子树中。


对一棵高度为 h 的 B树来说,B-TREE-INSERT 要做 O ( h ) 次磁盘存取。所占用的CPU总时间为 O ( t h ) = O ( t l o g t n ) 。因为 B-TREE-INSERT-NONFULL 是尾递归的,所以它也可以用一个 while 循环来实现,从而说明了在任何时刻,需要留在主存中的页面数为 O ( 1 )
这里写图片描述


猜你喜欢

转载自blog.csdn.net/rsy56640/article/details/80139166
今日推荐