目录
- 一、B-树(即B树)的定义及操作
- 1.1、定义
- 1.2、操作
- 1.2.1、查找
- 1.2.2、插入
- 1.2.3、删除
- 二、B+树的定义及操作
- 2.1、定义
- 2.2、操作
- 2.2.1、查找
- 2.2.2、插入
- 2.2.3、删除
- 三、B*树
一、B-树(即B树)的定义及操作
1.1、定义
B-tree即B树,B是Balanced,平衡的意思,因为B树的原英文名称为B-tree,国内很多人将其译为B-tree,所以B树就是B-树。
磁盘管理系统中的目录管理,以及数据库系统中的索引组织多数都采用B树这种数据结构。
一棵m阶的B树,或为空树,或是满足以下特性的m叉树:
- 树中每个结点至多有m棵子树;
- 若根结点不是叶子结点,则至少有两棵子树;
- 除根之外的所有非终端结点至少有[m/2](向上取整)棵子树;
- 所有叶子结点都出现在同一层次上,并且不带信息,通常称为失败结点(失败结点并不存在,指向这些结点的指针为空,引入失败结点是为了便于分析B树的查找性能);
- 所有非终端结点最少有[m/2]-1个关键字,最多有m-1个关键字,结点的结构如图所示:
其中,n为结点中关键字的个数,K为关键字,且;P为指向子树根结点的指针,且指针所指子树中所有结点的关键字均小于,所指子树中所有结点的关键字均大于。
对任一关键字K而言,相当于指向其左子树,相当于指向其右子树。
B树具有平衡、有序、多路的特点。
具体实现时,为记录其双亲结点,B树结点的存储结构通常会增加一个parent指针,指向其双亲结点,如图:
#define MaxSize 3
typedef struct BTnode{
int keynum;//结点中关键字的个数,即结点的大小
struct BTnode *parent;//指向双亲结点
ElemType key[m+1];//关键字向量,0号单元未用
struct BTnode *ptr[m+1];//子树指针向量
Record *recptr[m+1];//记录指针向量,0号单元未用
}BTnode, *BTree;
//B树查找结果类型
typedef struct{
BTnode *pt;
int i;//1...m,在结点中的关键字序号
int tag;//1:查找成功,0:查找失败
}Result;
1.2、操作
1.2.1、查找
算法思想:
将给定值key与根结点的各个关键字进行比较,由于该关键字序列是有序的,所以查找时可采用顺序查找,也可采用折半查找,查找时:
- 若,则查找成功;
- 若,则顺着指针所指向的子树继续向下查找;
- 若,则顺着指针所指向的子树继续向下查找;
- 若,则顺着指针所指向的子树继续向下查找。
- 如果在自上而下的查找过程中,找到了关键字key,则查找成功,如果直到叶子结点也未找到,则查找失败。
1.2.2、插入
思想:针对m阶高度h的B树,插入一个元素时,首先在B树中是否存在,如果不存在,即在叶子结点处结束,然后在叶子结点中插入该新的元素。
- 若该结点元素个数小于m-1,则直接插入;
- 若该结点元素个数等于m-1,引起结点分裂,以该结点中间元素为分界,取中间元素(若中间元素为两个,则随机选取一个)插入到父结点中;
- 重复上述操作,直到所有结点符合B树的规则,最坏的情况是一直分裂到根结点,生成新的根结点,高度增加1。
示例分析:
以5阶B树为例,5阶B树的规则:
- 2<=根结点的子树<=5;
- 3<=分支结点的子树<=5
- 1<=根结点元素个数<=4
- 2<=分支结点元素个数<=4
1.2.3、删除
首先查找B树中需删除的元素,如果该元素在B树中存在,则将该元素在其结点中进行删除;删除该元素后,首先判断该元素是否有左右孩子结点,如果有,则上移孩子结点中的某相近元素(“左孩子最右边的节点”或“右孩子最左边的节点”)到父节点中,然后是移动之后的情况;如果没有,直接删除。
- 某结点中元素数目小于[m/2]-1,则需要看其某相邻兄弟结点是否丰满;
- 如果丰满(结点中元素个数大于(m/2)-1),则向父节点借一个元素来满足条件;
- 如果其相邻兄弟都不丰满,即其结点数目等于(m/2)-1,则该结点与其相邻的某一兄弟结点进行“合并”成一个结点;
以5阶B树为例,详细讲解删除的动作:
如图依次删除依次删除【8】,【20】,【18】,【5】
二、B+树的定义及操作
2.1、定义
B+树是应文件系统所需而产生的B树的变形树。
一棵m阶B+树满足以下条件:
- 每个结点最多有M个子结点;
- 非叶子结点的根结点至少有两个子结点;
- 除根结点外的分支结点至少有[m/2]个子结点;
- 有k棵子树的非叶子结点有k个关键字,且关键字按照升序排列;
- 叶结点在同一层次中。
- 所有叶子结点增加一个链接指针链接在一起;
- 所有关键字及其映射数据都在叶子结点中出现。
优点:
B+树的插入过程与B树基本类似,区别在于: - 第一次插入两层结点,一层做分支,一层做根;
- B+树在分裂时,是将左半部分的数据保留,右半部分的数据放入新建兄弟结点中,并将新建结点中的最小值更新到父结点中。
2.2、操作
2.2.1、查找
若非终端结点上的关键字等于给定值, 并不终止,而是继续向下直到叶子结点。因此,在B+树中,不管查找成功与否,每次查找都是走了一条从根到叶子结点的路径。B+树查找的分析类似于B-树。
B+树不仅能够有效地查找单个关键字,而且更适合查找某个范围内的所有关键字。例如,在B+树上找出范围在[a, b]之间的所有关键字值。 处理方法如下:通过一次查找找出关键字 a, 不管它是否存在,都可以到达可能出现a的叶子结点,然后在叶子结点中查找关键字值等于a或大于a的那些关键字,对千所找到的每个关键字都有一个指针指向相应的记录,这些记录的关键字在所需要的范围。 如果在当前结点中没有发现大于b的关键字,就可以使用当前叶子结点的最后一个指针找到下一个叶子结点,并继续进行同样的处理,直至在某个叶子结点中找到大于b的关键字,才停止查找。
2.2.2、插入
仅在叶子结点上进行插入,当结点中的关键字个数大于m时要分裂成两个结点,它们所含关键字的个数分别为[(m+1)/2] 和 [(m+1)/2]并且,它们的双亲结点中应同时包含这两个结点中的最大关键字。
2.2.3、删除
B+树的删除也仅在叶子结点进行,当叶子结点中最大关键字被删除时,其 在非终端结点中的值可以作为一个 “分界关键字“ 存在。若因删除 而使结点中关键字的个数少于「m/2]时,其和兄弟结点的合 并过程亦和B-树类似。
三、B*树
B*树是在B+树的基础上,在B+树的非根和非叶子结点增加指向兄弟的指针,将结点的利用率从1/2提高到2/3。