设为首页 加入收藏

TOP

【C++研发面试笔记】12. 基本数据结构-B树簇
2016-10-08 11:31:16 】 浏览:302
Tags:研发 面试 笔记 12. 基本 数据结构 树簇

本节所说的B树并不是前面所说的二叉树(Binary Tree),而一类多路搜索树(B-Tree),其是为了解决二叉树只有两路的情况而提出,广泛应用于文件搜索(比如文件的目录树)。这类树主要分为B-树、B+树、B*树等等。

12.1 B-树

12.1.1 B-树的结构

B-树是一种平衡多路搜索树(并不是二叉的),其特征如下:

定义任意非叶子结点最多只有M个儿子,且M>2; 根结点的儿子数为[2, M]; 除根结点以外的非叶子结点的儿子数为[M/2, M]; 每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字) 非叶子结点的关键字个数=指向儿子的指针个数-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])的子树; 所有叶子结点位于同一层;

下图是一个M=3的情况下的B-树:
M=3的情况下的B-扫kyvc=" src="/uploadfile/Collfiles/20161004/20161004090111533.png" title="\" />

12.1.2 B-树的搜索

从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;

12.1.3 B-树的特性

关键字集合分布在整颗树中; 任何一个关键字出现且只出现在一个结点中; 搜索有可能在非叶子结点结束; 其搜索性能等价于在关键字全集内做一次二分查找; 自动层次控制;

由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的至少利用率,其最低搜索性能为O(logN),N为关键字总数。因此B-树的性能总是等价于二分查找(与M值无关),也就没有二叉树的平衡问题。
由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点。
当删除结点时,需将两个不足M/2的兄弟结点合并。


12.2 B+树

12.2.1 B+树的结构

由于B-树在插入或删除结点时,可能需要进行兄弟结点的合并或分裂成两个兄弟结点的操作,此时需要通过向上访问父结点,从而达到兄弟结点,这样需要额外的结点复制操作,因此我们引入B+树。
B+树是B-树的变体,也是一种多路搜索树,其定义基本与B-树同,除了:

非叶子结点的子树指针与关键字个数相同; 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间); 为所有叶子结点增加一个链指针,指向相邻结点; 所有关键字都在叶子结点出现;

下图是一个M=3的情况下的B+树:
M=3的情况下的B+树

12.2.2 B+树的搜索

B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找

12.2.3 B+树的特性

所有关键字都出现在叶子结点的链表中,且链表中的关键字恰好是有序的; 不可能在非叶子结点命中; 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层; 更适合文件索引 系统;比如对已经建立索引的 数据库记录,查找10<=id<=20,那么只要通过根节点搜索到id=10的叶节点,之后只要根据叶节点的链表找到第一个大于20的就行了,比B-树在查找10到20内的每一个时每次都从根节点出发查找提高了不少效率。

12.3 B*树

12.3.2 B*树的结构

B*树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;
B* 树定义了非叶子结点关键字个数至少为(2/3)* M,即块的最低使用率为2/3(代替B+树的1/2);
M=3的情况下的B*树

12.3.2 B*树与B+树的区别

B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;
B* 树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针; 所以,B*树分配新结点的概率比B+树要低,空间使用率更高;


12.4 B树簇总结

B树:二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;
B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
B* 树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;

12.4.1 B+/B* Tree应用

1) B+tree的磁盘读写代价更低
B+-tree的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘快。而B+ 树内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B 树就比B+ 树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。
2) B+tree的查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
数据库索引采用B+树的主要原因是,B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。正是为了解决这个问题,B+树应运而生。B+树只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。


12.5 多叉树与二叉树的转换

12.5.1 多叉树转换成二叉树

1加线:所有兄弟节点之间加线
2去线:保留树中每个结点与它第一个孩子的连线,删除其与其他孩子的连线
3层次调整:以根结点为轴心,将整棵树旋转,使之层次分明。
多叉树转换成二叉树

12.5.2 二叉树转换成多叉树

而将二叉树转换为树,正好是一个相逆的过程。
1加线。若某结点X的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点…,都作为结点X的孩子。将结点X与这些右孩子结点用线连接起来。
2去线。删除原二叉树中所有结点与其右孩子结点的连线。
3层次调整。
二叉树转换成多叉树

M阶B-树中含有N个关键字,最大深度为
M阶B-树的最大深度

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++研发面试笔记:基本数据结构-.. 下一篇【C++研发面试笔记】1. C++常见关..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目