数据结构 多路查找树 ---------B树和B+树的简单介绍

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u011109881/article/details/80344606

参考链接:微信公众号 程序员小灰
https://mp.weixin.qq.com/s/rDCEFzoKHIjyHfI_bsz5Rw
https://mp.weixin.qq.com/s/jRZMMONW3QP43dsDKIV9VQ
本文为半原创,结合了 程序员小灰和自己的一些理解和总结,如有错误,请帮忙指出,感谢。

B树的简单介绍

前面我们已经介绍了BST AVL树 BRT,那么,为什么要B树和B+树呢?他们和之前介绍的三种树有何区别?
其实最明显的差异体现在多叉上,前面介绍的三种树都是二叉树。而B树和B+树是多路查找树,目的是优化磁盘访问的速度。之前介绍的三种树是索引树,节点直接的连接可以理解为指针,即每个节点在磁盘中散列存储,像这样
这里写图片描述
想要访问红色节点,需要先访问绿色节点,根据绿色节点的左子树索引查找到红色节点位置,再访问红色节点。
我们看下之前三种树的磁盘访问
假设访问如下的树的节点10
这里写图片描述
第一次访问
这里写图片描述
第二次访问
这里写图片描述
第三次访问
这里写图片描述
第四次访问
这里写图片描述
不难看出,一个节点的访问磁盘的次数取决于该节点在树上的高度。

因此,为了减少访问磁盘的次数,就必须降低树的高度,使树变得“矮胖”,而B树和B+树都具有矮胖的特点

B树的定义

一个m阶的B树具有如下几个特征:(m阶中的m可以理解为B树的分叉数,比如5阶B树最大分叉是5)

  • 1.根结点至少有两个子女。
  • 2.每个中间节点包含k-1个元素和k个孩子,其中 m/2向上取整 <= k <= m
  • 3.每一个叶子节点包含k-1个元素,其中 m/2向上取整 <= k <= m
  • 4.所有的叶子结点都位于同一层。
  • 5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。

实例

这是一个B树
这里写图片描述
但实际上应该是这样的
这里写图片描述
每个节点应该有n个数据域和n+1个指针域组成,叶子节点的指针域都为空。但为了作图简单,就不将指针域画出来了
实际m阶B树节点结构如下:
这里写图片描述
除了叶子节点,其余节点的指针域可以通过双亲节点或者孩子节点的索引看出来。

以节点2,6为例,我们看到
1<2
2<3<5<6
6<8

B树的磁盘访问

以上面的树为例
第1次磁盘IO:
这里写图片描述
在内存中定位(和9比较):
这里写图片描述
第2次磁盘IO:
这里写图片描述
在内存中定位(和2,6比较):
这里写图片描述
第3次磁盘IO:
这里写图片描述
在内存中定位(和3,5比较):
这里写图片描述
其实从图中可以看出,相同的数字,构建的平衡二叉树和B树的高度相差一。
从上面我们看出
AVL树进行了三次IO访问和5次内存比较
B树进行了4次IO访问和4次内存比较。
我们知道,内存的访问速度是远远大于外存(IO)访问的速度的,因此,当m阶B树的m越大,单一节点的元素树越多,B树的高度就越低,IO访问的次数就越少。但是,也不是m越大越好,当m大到m个数据,外存一个磁盘页装不下就糟了。因此m是受磁盘页大小限制的

B树的插入和删除

B树也是一种自平衡树
插入分以下几种情况
1.可以直接插入的情况
2.插入后破环平衡(即破环B树的定义中的任意一个性质),需要进行分裂
删除
1.可以直接删除
2.直接删除后破环平衡,先向左右子树“借”节点
3.直接删除后破环平衡,左右子树“借”不到节点,合并子树
插入删除操作比较复杂,具体示例请参照参考链接

B树的应用

文件系统索引和部分非关系型数据库,如MongoDB。但是大多数关系型数据库使用B+树作为索引。那么B+树和B树相比有何优势呢?
提前透露一下,B+树适用于范围查找

B+树

定义

  • 1.有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
  • 2.所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
  • 3.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
    如下便是一个B+树
    这里写图片描述
    可以看到:
    1 每一个双亲节点的元素均会出现在孩子节点中,并且是子节点的最大(或最小)元素,也就是说,B+树存在重复元素。
    2 其次可以看到叶子节点用指针相连,形成一个链表。
    3 另外,要注意,在当前树中,根节点的15代表整棵树的最大元素,所以,对B+树做插入是,要保证最大元素必定在根节点出现过(如果双亲节点的元素均会出现在孩子节点中,并且是子节点的最小元素,则需要反过来)
    4 由于每个双亲节点均在孩子节点出现,也就是说,叶子节点包含了全部节点(当然,还包括没有在双亲节点出现的元素)
    这里写图片描述

B+树的查找

之前我看了B树查找的过程,那么在B+树中是如何查找元素3呢?
这里写图片描述
一共进行了3次IO
首先,由于B+树只有根节点存储了元素数据,所以必须查到叶子节点。其次由于非叶子节点只存储索引,所以可以存储的索引树更多,能够使得B+树的高度更低,IO次数更少。

相比于B树, B+树的优势

我们看下B树的范围查找(3-11)
这里写图片描述
自顶向下,查找到范围的下限(3)–>中序遍历到元素6–>中序遍历到元素8–>中序遍历到元素9–>中序遍历到元素11,遍历结束

同样的元素构成的B+树的相同范围查找如下
这里写图片描述
自顶向下,查找到范围的下限(3)–>通过链表指针,遍历到元素6, 8–>通过链表指针,遍历到元素9, 11,遍历结束
所以说,相比于B树 B+树的优势有:

1.单一节点存储更多的元素,使得查询的IO次数更少。(单一节点存储更多的元素是因为B+的非叶子节点不存储数据,存的是索引)

2.所有查询都要查找到叶子节点,查询性能稳定。(同样是因为非叶子节点不存储数据,数据只有叶子节点有)

3.所有叶子节点形成有序链表,便于范围查询。

二叉搜索树 平衡二叉树 红黑树 B树 B+树的简单对比

简称 特点 平衡性 查找性能 插入 删除 优势 应用
二叉搜索树 BST 索引树,由于构建不稳定,查找效率不稳定 不稳定,跟具体构建树相关,最好O(logn),最差O(N) 直接插入 删除的是叶子节点或只有左或右子树比较简单,如同时存在左右子树,那么要找到前驱或后继代替原先节点,再删除 性能高于线性查找 暂未找到,欢迎补充
平衡二叉树 AVL 索引树,BST进化版,自平衡,查找效率稳定 最好最坏都是O(lgn) 最多需要2次旋转 最多需要lgN次旋转(参考,本人未证实) 高度平衡,进一步提升查询效率 暂未找到,欢迎补充
红黑树 RBT 索引树,BST进化版,自平衡,查找效率相对稳定 基本维持在O(lgn),最坏比AVL略差(2lg(n+1)) 最多需要2次旋转,3次变色 最多需要3次旋转(参考,本人未证实) 自平衡,进一步提升查询效率 Java中TreeSet和TreeMap,JDK 8以后,HashMap的设计
B树 ^ 多路查找树,提升IO效率,索引树进化而来 IO次数取决于高度,减少IO次数 1.可以直接插入的情况2.插入后破环平衡,需要进行分裂 1.可以直接删除2.直接删除后破环平衡,先向左右子树“借”节点3.直接删除后破环平衡,左右子树“借”不到节点,合并子树 降低IO次数 文件系统索引和部分非关系型数据库,如MongoDB
B+树 ^ 多路查找树,提升IO效率,B树进化版 进一步减少IO次数 类似B树 类似B树 进一步降低IO次数,范围查询方便,查询性能稳定 大多关系型数据库使用B+树作为索引

另外提一下哈夫曼树,上面的几个都是查找树,而哈夫曼树的设计不是为了查找,而是为了减少编码长度。哈夫曼树多应用于压缩技术(哈夫曼编码)

猜你喜欢

转载自blog.csdn.net/u011109881/article/details/80344606
今日推荐