文章目录

  • 搜索结构
  • B树
    • B树的插入
    • B树的遍历
    • B树的性能
  • B+树
    • B+树的插入
    • B+树的遍历
  • B*树
    • B*树的插入
  • 总结

搜索结构

  • 如果我们有大量的数据需要永久存储,就需要存储到硬盘之中。
  • 但是硬盘的访问速度远远小于内存,并且由于数据量过大,无法一次性加载到内存中。

此时,就可以考虑将数据存储在硬盘中,而数据的地址则加载到内存中,通过某种搜索结构进行存储,使用时只需要通过该结构查找到地址,在通过地址去找到对应的数据即可。

常用的几种搜索结构:二叉搜索树、AVL树、红黑树、哈希、位图、布隆过滤器。

考虑到查找性能以及内存消耗,其中适合这种场景的只有平衡二叉搜索树(AVL、红黑树)。

但即使平衡二叉搜索树的搜索性能能达到 O(log2N),由于数据量过于庞大,例如存储了 10亿个数,则可能最多需要查找 30次。这个数字看起来不是很多,因为之前我们比较的是内存的速度,即使是 10亿个数 也能一瞬间查找完。但是对于硬盘来说,由于硬盘的速率低,每一次 IO 都意味这大量的损耗,所以这种方法也不太合适。

如果想要提高查找的效率,那么唯一的方法就是压缩树的高度,而压缩的方法,就是将二叉树变为 M叉树,也就是使用到 M路平衡搜索树——B树


B树

B树 即一棵平衡的 M路平衡搜索树(M > 2),可以是 空树 或者满足以下性质:

  • 根节点至少有 2 个孩子、最多有 M 个孩子;
  • 除根结点外的非叶节点有 i 个孩子, M/2(上取整) <= i <= M
  • 每个非叶节点有 j 个关键字, M/2-1(上取整) <= j <= M-1 ,并且以升序排列
  • key[i]key[i+1] 之间的孩子节点的值介于 key[i]、key[i+1] 之间;
  • 所有的叶子节点都在同一层。

对照一棵 M=3B树 来理解:

节点实现

template<class K, int M = 3>
struct BTreeNode
{K _keys[M]; // 存放元素BTreeNode<K, M>* _pSub[M+1]; // 存放孩子节点,注意:孩子比数据多一个BTreeNode<K, M>* _pParent; // 在分裂节点后可能需要继续向上插入,为实现简单增加parent域size_t _size; // 节点中有效元素的个数BTreeNode(): _pParent(NULL), _size(0){for(size_t i = 0; i <= M; ++i)_pSub[i] = NULL;}
};

B树的插入

下面拿一个 M=3三叉B树 来举例子。(PS:三叉树即每个节点至多 3 个孩子,2keykey 的数量永远比孩子少一个)

假设使用以下数据构建B树 {63, 131, 85, 39, 148, 31, 111}(B树从叶子节点的位置进行插入):


首先依次插入前三个节点,当插入到第三个时,由于三叉树的 key 只能有 2 个,所以此时会采取分裂的方法来维持树的平衡。

B树分裂的规则是:创建一个兄弟节点,拷贝右半区间的数据到兄弟节点,左半区间保留,中位数放到父亲节点(如果没有则创建新的根节点)。

B树AVL 的旋转、红黑树的旋转+变色不一样,它使用了分裂的方法来维持树的平衡,这样的好处是既能做到平衡,也保证了非根节点至少有一半的空间利用率

所以此时按照上面的规则进行分裂:

接着插入 39,148

然后插入 31,开始分裂:

接着插入111,此时再次发生分裂:

此时可以看到,叶子节点已经分裂成了 4 个,并且根节点的 key 也达到了 3 个,不符合 B树 的性质:每个根节点最多有 M 个孩子、M - 1 个key,因此继续分裂:

此时,树重新平衡。从上面可以看出来,本质上B树的设计还是参考了二叉搜索树。


B树的遍历

B树的有序遍历还是通过中序遍历来完成,不过需要通过队列或者递归遍历完这个节点的所有的key

void _InOrder(PNode pRoot)
{if(NULL == pRoot)return;for(size_t i = 0; i < pRoot->_size; ++i){_InOrder(pRoot->_pSub[i]);cout << pRoot->_keys[i] << " ";}_InOrder(pRoot->_pSub[pRoot->_size]);
}

B树的性能

作为一个M路平衡搜索树,B树的搜索性能达到了 log⁡M+1N\log_{M+1}NlogM+1​N ~ log⁡M/2N\log_{M/2}NlogM/2​N 之间,查找到指定关键字的方法是:

  • 在根结点所包含的关键字 K1、…、Kn 查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;
  • 否则,一定可以确定要查找的关键字在 KiKi+1之间,Pi 为指向子树根节点的指针,此时取指针 Pi 所指的结点继续查找,直至找到,或指针 Pi 为空 时查找失败。

比起二叉平衡搜索树,速度快了一大截,并且大大的减少了硬盘 IO 的次数,所以在文件系统以及数据库索引等方面使用的都是这种数据结构。


B+树

B+树是B树的变形,主要性质如下:

  • 其定义基本与B树相同
  • 非叶子节点的 孩子key 个数相同(简化规则)
  • 非叶子节点由叶子节点的最小值构成(充当索引,所以不可能在非叶子节点命中
  • 所有数据都出现在叶子节点,并且所有叶子节点都链接起来,同时是有序的。(方便遍历)


B+树的插入

这里为了方便,使用 三阶B+树 举例子,这里还是使用同样的数据。{63, 131, 85, 39, 148, 31, 111}

首先插入 63,并把 63 作为父节点的索引:

接着插入 131,85 :

当插入 39 时,发生分裂,分裂规则与之前略有区别。

B+树的分裂规则:因为此时父节点存储的是索引,所以此时只会将左半部分数据保留,右半部分数据放入新建的兄弟节点,并且会向上更新父节点的索引。


当插入 111 时,发生分裂:


B+树的遍历

从上面可以看出来,B+树的主要特点其实就是更方便进行遍历,因为其将所有数据存储在叶子节点,所有非叶子节点就相当于一个索引。其所有叶子节点连接起来,像遍历链表一样遍历B+树。这样的结构使得B+树的查找相当于对关键字全集做一次二分查找。

所以通常文件的索引系统都会采用B+树的结构。


B*树

B*树则又是对B+树的变形,其性质如下:

  • 其定义基本与B+树相同
  • 将非叶子节点也连接起来
  • 分裂方式再次修改,保证每个节点中 key 的数量 [2/3 * M,M](提高空间利用率,从1/2提升到了2/3)


B*树的插入

B*树再次修改了插入规则,规则修改为:

  • 如果当前节点数据已满而兄弟节点未满,则将数据放入兄弟节点,而当两个节点都满了之后再进行分裂;
  • 分裂时,在原节点与兄弟节点之间创建新节点,从两个节点分别取出 1/3 的数据放入新创建的结点。

还是原来那些数据 {63, 131, 85, 39, 148, 31, 111} :

接下来插入 39

插入 148、31

此时插入111,发生分裂,从两边各取走 1/3 的数据:

从上面可以看出,B*树的最大改进就是将B+树的空间利用率从1/2提升到了2/3,并且对非叶子节点也进行了连接,查找更加便利。


总结


数据结构 | B树、B+树、B*树相关推荐

  1. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-AVL树(一)

    前言 [从蛋壳到满天飞]JS 数据结构解析和算法实现,全部文章大概的内容如下: Arrays(数组).Stacks(栈).Queues(队列).LinkedList(链表).Recursion(递归思 ...

  2. 数据结构大总结系列之B树和R树

    原文:http://www.cnblogs.com/javaspring/archive/2012/08/14/2656223.html 一,B-树 B树是为磁盘或其他直接存储辅助存储设备而设计的一种 ...

  3. 【数据结构】B-Tree, B+Tree, B*树介绍

    [摘要] 最近在看Mysql的存储引擎中索引的优化,神马是索引,支持啥索引.全是浮云,目前Mysql的MyISAM和InnoDB都支持B-Tree索引,InnoDB还支持B+Tree索引,Memory ...

  4. 对分查找的最多次数_Java数据结构与算法:多路查找树

    作者:subeiLYhttps://blog.csdn.net/m0_46153949/article/details/106742330 本章思维导图 二叉树与B树 二叉树的问题分析二叉树的操作效率 ...

  5. 【数据结构-树】1.树与森林(树的遍历、树的存储方法、并查集的实现)

    树的定义 树是一种数据结构,它是由 n(n>=1)n(n>=1)n(n>=1) 个有限结点组成一个具有层次关系的集合.把它叫做 "树" 是因为它看起来像一棵倒挂的 ...

  6. 数据结构与算法(C++)– 树(Tree)

    数据结构与算法(C++)– 树(Tree) 1.树的基础知识 树(tree): 一些节点的集合,可以为空集 子树(sub tree): 树的子集 根(root): 树的第一个节点 孩子和父亲(Chil ...

  7. 数据结构与算法 / 默克尔树

    最近在学习 git 原理时,涉及到了默克尔树,这里总结下该数据结构. 默克尔树于 1979 年由美国计算机科学家拉尔夫·默克尔(Ralph Merkle)提出,本质上是一种树状数据结构,由数据块.叶子 ...

  8. 数据结构-----基于双数组的Trie树

    Trie树简介 Trie树也称字典树,在字符串的查找中优势比较明显,适用于在海量数据中查找某个数据.因为Trie树的查找时间和数据总量没有关系,只和要查找的数据长度有关.比如搜索引擎中热度词语的统计. ...

  9. 『ACM-算法-数据结构』信息竞赛进阶指南--树状数组 (模板)

    写在前面: 我们是主要是讲算法模板,即实现的代码,并不讲实现的原理 什么是树状数组? 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度 ...

  10. 数据结构与算法(C#实现)系列---树

    Heavenkiller(原创) 首先我们给树下一个定义: 树是一个有限的.非空的结点集, T={r} or T1 or T2 or-or Tn 它具有下列性质: 1.集合指定的结点r叫做树的根结点 ...

最新文章

  1. 一分钟详解点云配准ICP方法
  2. 各样本观察值均加同一常数_对色师傅分享:如何使不同观察者在灯箱下观察的色光一致?...
  3. keil2c语言使用教程,Keil教程(2)
  4. iview 使用笔记
  5. Java微服务篇3——Lucene
  6. java使用netty
  7. bzoj2456: mode
  8. WEB安全基础理论笔记(幕布)
  9. poi导出如何设定宽度_POI导出excel列宽自适应
  10. VBScript 教程
  11. QT 学习之饼状图实现
  12. 计算机优先启动项,大白菜u盘装系统bios设置优先启动项操作方法
  13. 卷积神经网络学习路线(十三)| CVPR2017 Deep Pyramidal Residual Networks
  14. PDF文件太大无法上传,如何压缩变小?
  15. Web安全测试:使用火狐浏览器修改请求参数
  16. PyTorch 加载预训练权重
  17. mybatis 绑定失败:Invalid bound statement (not found): com.demo.service.api.dao.SysUserMapper.insert
  18. 复变函数与积分变换---复数
  19. 【韩顺平】设计模式七大原则
  20. 为什么你该学习编程了?

热门文章

  1. webpack最新版本_webpack小结-开发环境构建优化
  2. 火星云分发全网视频_好用的短视频一键分发软件,让工作效率提高10倍
  3. 计算机学院寝室文明风景线活动,小猿关注 | 营造良好学风 打造和谐宿舍 ——计算机学院开展学风主题教育暨文明宿舍评选活动...
  4. 地表反射率影响因素_【热岛强度可影响城市夏季降水落区】
  5. django 学习 (一)
  6. Asterisk realtime 之SIP用户动态写入mysql 数据库
  7. linux内核I2C子系统学习(一)
  8. python能不能爬数据库_python爬取数据后不能写入到数据库中
  9. python什么是空类型_在Python中创建真正的空类型
  10. listview刷新_Flutter NestedScrollView 滑动折叠头部下拉刷新效果