【数据结构】B树、B+树与B*树详解

B树

1.B树的定义

  • B树(B-tree)是对2-3树数据结构的扩展,又称为多路平衡查找树,它的一个节点可以拥有多于2个子节点的二叉查找树。与自平衡二叉查找树不同,

  • B树是一种自平衡树数据结构,可以保持数据排序,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构

  • B树针对读写大数据块的系统进行了优化。B树的算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。

注:有人说B-树,其实就是B树,因为B树的原英文名称为B-tree

2.B树的性质

一棵m阶的B 树 (m叉树)的性质

  • 树中每个结点最多含有m个孩子(m>=2);

  • 若根结点不是叶子结点,则至少有2个孩子

  • 除根结点和叶子结点外,其它每个结点至少有[ceil(m / 2)]个孩子

  • 所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息

  • 每个非终端结点中包含有n个关键字信息,并且以升序排列

B树实示例图
《【数据结构】B树、B+树与B*树详解》

注:小红方块表示这个17文件内容在硬盘中的存储位置;p1表示指向17左子树的指针

3.B树的插入与删除

(1)插入的步骤

  • 插入一个元素时,首先在B树中是否存在,如果不存在,即在叶子结点处结束,然后在叶子结点中插入该新的元素

  • 如果叶子结点空间足够,这里需要向右移动该叶子结点中大于新插入关键字的元素,如果空间满了以致没有足够的空间去添加新的元素,则将该结点进行“分裂”,将一半数量的关键字元素分裂到新的其相邻右结点中,中间关键字元素上移到父结点中(当然,如果父结点空间满了,也同样需要“分裂”操作),

  • 当结点中关键元素向右移动了,相关的指针也需要向右移。如果在根结点插入新元素,空间满了,则进行分裂操作,这样原来的根结点中的中间关键字元素向上移动到新的根结点中,因此导致树的高度增加一层

实例:构造一个包含C N G A H E K Q M F W L T Z D P R X Y S元素的5阶B树
(关键字小于2个就合并,超过4个就分裂)

1>结点空间足够,4个字母插入相同的结点中

《【数据结构】B树、B+树与B*树详解》

2>插入H时,结点发现空间不够,以致将其分裂成2个结点,移动中间元素G上移到新的根结点中,把A和C留在当前结点中,而H和N放置新的其右邻居结点中

《【数据结构】B树、B+树与B*树详解》

3>插入E,K,Q时,不需要任何分裂操作

《【数据结构】B树、B+树与B*树详解》

4>插入M需要一次分裂,因为M恰好是中间关键字元素,所以向上移到父节点中

《【数据结构】B树、B+树与B*树详解》

5>插入F,W,L,T不需要任何分裂操作

《【数据结构】B树、B+树与B*树详解》

6>插入Z时,最右的叶子结点空间满了,需要进行分裂操作,中间元素T上移到父节点中,注意通过上移中间元素,树最终还是保持平衡,分裂结果的结点存在2个关键字元素。

《【数据结构】B树、B+树与B*树详解》

7>插入D时,导致最左边的叶子结点被分裂,D恰好也是中间元素,上移到父节点中,然后字母P,R,X,Y陆续插入不需要任何分裂操作

《【数据结构】B树、B+树与B*树详解》

8>最后,当插入S时,含有N,P,Q,R的结点需要分裂,把中间元素Q上移到父节点中,但是情况来了,父节点中空间已经满了,所以也要进行分裂,将父节点中的中间元素M上移到新形成的根结点中

《【数据结构】B树、B+树与B*树详解》

(2)删除的步骤

  • 首先查找B树中需删除的元素,如果该元素在B树中存在,则将该元素在其结点中进行删除,

  • 删除该元素后,首先判断该元素是否有左右孩子结点,如果有,则上移孩子结点中的某相近元素(“左孩子最右边的节点”或“右孩子最左边的节点”)到父节点中,然后是移动之后的情况;如果没有,直接删除后,移动之后的情况。

  • 删除元素,移动相应元素之后,如果某结点中元素数目(即关键字数)小于ceil(m/2)-1,则需要看其某相邻兄弟结点是否丰满(结点中元素个数大于ceil(m/2)-1)

  • 如果丰满,则向父节点借一个元素来满足条件;如果其相邻兄弟都刚脱贫,即借了之后其结点数目小于ceil(m/2)-1,则该结点与其相邻的某一兄弟结点进行“合并”成一个结点,以此来满足条件。

实例:删除B树中的h、r、p、d元素

《【数据结构】B树、B+树与B*树详解》

B+树

1.B+树的定义

B+-tree是应文件系统所需而产生的一种B-tree的变形树。

2.B+树与B树区别

  • B+树中有n棵子树的结点中含有n个关键字,而B 树是n棵子树有n-1个关键字

  • B+树所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,所有的叶子结点和相连的节点使用链表按从小到大的顺序相连,便于区间查找和遍历。而B 树的叶子节点并没有包括全部需要查找的信息。

  • B+树所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)

  • B+树的叶子结点都是相链的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。

  • B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子几点上关联的数据也具有更好的缓存命中率。B树的优点:由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。

B+树示例图
《【数据结构】B树、B+树与B*树详解》

B*树

1.B*树的定义

B*-tree是B+-tree的变体,在B+ 树非根和非叶子结点再增加指向兄弟的指针

2.B*树的性质

  • B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2)

  • B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针。

  • B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针。

  • B*树分配新结点的概率比B+树要低,空间使用率更高;

B*树示例图
《【数据结构】B树、B+树与B*树详解》

B树和B+树的应用场景

1.文件存储系统

在B+树中,内节点只存储导航用到的key,并不存储具体值,这样内节点个数较少,能够全部读取到主存中,外接点存储key及值,并且顺序排列,具有良好的空间局部性。所以B及B+树比较适合与文件系统的数据结构。

2.数据库系统

Mysql的MyISAM和InnoDB两个存储引擎的索引实现方式:

  • MyISAM

    • MyISAM引擎使用B+ Tree作为索引结构,叶节点存放的是数据记录的地址。
    • MyISAM引擎的辅助索引(二级索引)和主索引在结构上没有区别,只是辅助索引的key可以重复,叶节点上存放的也是数据记录的地址。
    • MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。
  • InnoDB

    • InnoDB中表数据本身就是按B+ Tree组织的一个索引结构,叶节点存放的就不是数据记录的地址,而是完整的数据记录。所以InnoDB这种存储方式,又称为聚集索引,使得按主键的搜索十分高效,但二级索引搜索需要检索两遍索引:首先二级索引获得主键,然后用主键到主索引中检索到数据记录。
    • 因为主键是InnoDB表记录的”逻辑地址“,所以InnoDB要求表必须有主键,MyISAM可以没有。

本人才疏学浅,若有错,请指出
谢谢!

参考资料:

1.B树详解
2.平衡查找树之B树
3.B树详解——维基百科
4.简单清晰的B树、Trie树详解
5.MySQL索引背后的数据结构及算法

    原文作者:B树
    原文地址: https://blog.csdn.net/baiye_xing/article/details/75634135
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞