https://blog.csdn.net/aqzwss/article/details/53074186
https://yq.aliyun.com/articles/38345
关于这B树以及B树的两种变体,其实很好区分,
相比B树,B+树不维护关键字具体信息,不考虑value的存储,所有的我们需要的信息都在叶子节点上,
B*树在B+树的基础上增加了非叶子节点兄弟间的指针,在某些场景效率更高.
B树(B-tree),即B-树
注意B树也就是B-树,B树的英文是B-tree,很多地方直译成了B-树,其实B-树和B树是同一种树。
B树是为了磁盘或其它存储设备而设计的一种多叉平衡查找树。
(1)B-Tree的接点结构
B-tree中,每个结点包含:
1、本结点所含关键字的个数;
2、指向父结点的指针;
3、关键字;(关键字用数组存储)
4、指向子结点的指针数组;
#define Max l000 //结点中关键字的最大数目:Max=m-1,m是B-树的阶
#define Min 500 //非根结点中关键字的最小数目:Min=m/2-1
typedef int KeyType; //KeyType关键字类型由用户定义
typedef struct node{ //结点定义中省略了指向关键字代表的记录的指针
int keynum; //结点中当前拥有的关键字的个数,keynum<<Max
KeyType key[Max+1]; //关键字向量为key[1..keynum],key[0]不用。
struct node *parent; //指向双亲结点
struct node *son[Max+1];//指向孩子结点的指针数组,孩子指针向量为son[0..keynum]
}BTreeNode;
typedef BTreeNode *BTree;
(2)B-tree的特点
B-tree是一种多路搜索树(并不是二叉的),对于一棵M阶树:
定义任意非叶子结点最多只有M个孩子;且M>2;
根结点的孩子数为[2, M],除非根结点为叶子节点;
除根结点以外的非叶子结点的儿子数为[M/2, M];(M/2向上取整,不是向下)
非叶子结点的关键字个数=指向儿子的指针个数-1;
每个非叶子结点存放至少M/2-1(取上整)和至多M-1个关键字;
非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
所有叶子结点位于同一层;
B-树的特性:
1.关键字集合分布在整颗树中;
2.任何一个关键字出现且只出现在一个结点中;
3.搜索有可能在非叶子结点结束;
4.其搜索性能等价于在关键字全集内做一次二分查找;
5.自动层次控制;
以M=3的一棵3阶B树为例:
一棵包含了24个英文字母的5阶B树的结构:
(3)B-tree高度与复杂度
B树的高度是,而不是其它几种树的H=log2n,其中T为度数(每个节点包含的元素个数),即所谓的阶数,n为总元素个数或总关键字数。
B树查找的时间复杂度为O(Log2-N),下面是参考推导过程:
其中M为设定的非叶子结点最多子树个数,N为关键字总数;所以B-树的性能总是等价于二分查找(与M值无关),也就没有AVL树平衡的问题。
B+树(B±tree)
1.有n棵子树的结点中含有n个关键字**,每个关键字不保存数据,只用来索引,所有数据都保存在叶子节点。**
2.所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
3.所有的非终端结点可以看成是索引部分,结点中仅含其子树(根结点)中的最大(或最小)关键字。
通常在B+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点。
B+树是B-树的变体,也是一种多路搜索树:
1.其定义基本与B-树同,除了:
2.非叶子结点的子树指针与关键字个数相同;
3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
5.为所有叶子结点增加一个链指针;
6.所有关键字都在叶子结点出现;
如:(M=3)
B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;
B+的特性:
1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
2.不可能在非叶子结点命中;
3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
4.更适合文件索引系统;
用途
B+ 树通常用于数据库和操作系统的文件系统中。NTFS, ReiserFS, NSS, XFS, JFS, ReFS 和BFS等文件系统都在使用B+树作为元数据索引。B+ 树的特点是能够保持数据稳定有序,其插入与修改拥有较稳定的对数时间复杂度。B+ 树元素自底向上插入。
B*树:
*是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;
B*树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为2/3(代替B+树的1/2);
B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;B树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;所以,B树分配新结点的概率比B+树要低,空间使用率更高;
小结:
B-树:
多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点**;所有关键字在整颗树中出现**,且只出现一次,非叶子结点可以命中;
B+树:
在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
B*树:
在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;