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

接上,算法导论 - 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) ( x . n + 1 ) 路选择


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

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

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


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

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


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


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



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


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


定义B-树存储结构(要求m3;为方便操作,结点中增加双亲结点指针域,最底层的Fail结点用NULL指针表示并且所有结点均存储于内存)。定义B-树插入关键字函数、删除关键字函数、查找关键字函数以及按层次遍历输出B-树所有结点的函数。主函数定义菜单(1.插入关键字 2.删除关键字 3. 查找关键字 4.层次遍历输出B-树所有结点 5.结束程序)。 1. 插入关键字功能的输入为一个关键字,输出为新插入关键字所在结点的信息。 要求结点信息输出格式如下所示: (R102, n, K1, K2, …, Kn) R102表示结点位置,R表示根结点指针;第一个数字1表示根结点的A[1]指针,第二个数字0表求R->A[1]所指结点的A[0]指针,第三个数字2表示R->A[1]->A[0]所指结点的A[2]指针,即该结点指针为: R->A[1]->A[0]->A[2](该结点在第4层上)。n为该结点的关键字数目,K1, K2, …, Kn为该结点中n个非递减有序的关键字。 2. 删除关键字功能的输入为一个关键字,输出为删除成功与失败的信息。 3. 查找关键字功能的输入为一个关键字,输出为查找成功与失败的信息,查找成功时,应输出关键字所在结点信息(结点信息输出方法同1.)。 4. 按层次遍历输出B-树所有结点功能的输入为一个字符文件名,输出为该字符文件,字符文件中,一个结点的信息输出一行(结点信息输出方法同1.),结点输出次序为按层次号由小到大并且同层结点从左向右。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值