一. B树的定义

1.1. B树概念与使用场景

B树(B-tree,所以很多人又称为B-树)
是一种自平衡的树,一个节点可以拥有2个以上的子节点,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。

与自平衡二叉查找树不同,B树的每个节点可以包含大量的关键字信息和分支,便于降低自己的高度,让自己更胖更矮,更加适用于读写相对大的数据块的存储系统,例如磁盘,可以减少定位记录时所经历的中间过程,从而加快存取速度。B树这种数据结构可以用来描述外部存储。这种数据结构常被应用在数据库和文件系统的实现上。

在B树中,内部(非叶子)节点可以拥有可变数量的子节点(数量范围预先定义好)。当数据被插入或从一个节点中移除,它的子节点数量发生变化。为了维持在预先设定的数量范围内,内部节点可能会被合并或者分离。因为子节点数量有一定的允许范围,所以B树不需要像其他自平衡查找树那样频繁地重新保持平衡,在这点名批评红黑树,但是由于节点没有被完全填充,可能浪费了一些空间。

B树的优势如下:

  • 使关键字保持排序顺序以进行顺序遍历
  • 使用分层索引来最大程度地减少磁盘读取次数
  • 使用部分完整的块来加快插入和删除
  • 使用递归算法使索引保持平衡

总之,B树操作不是很复杂,用途很广泛,正道的光,照在了数据结构的路上

1.2. B树定义


根据 Knuth 的定义,一个 m 阶的B树是一个有以下属性的树:

  • 每一个节点最多有 m 个子节点
  • 每一个非叶子节点(除根节点)最少有 ⌈m/2⌉ (向上取整)个子节点
  • 如果根节点不是叶子节点,那么它至少有两个子节点
  • 有 k 个子节点的非叶子节点拥有 k − 1 个键
  • 所有的叶子节点都在同一层,叶子节点不包含任何关键字信息

但是其实文献中B树的术语并不统一
欧美在术语统一这方面一直都可以的 : )

术语的定义不一致。

  • Bayer & McCreightComer等人将B树的定义为非根节点拥有键的最小数量。Folk & Zoellick指出这一术语是模糊不清的。一个 3 阶B树键的最大数量可能为 6 或 7。 Knuth 通过将阶定义为最大数量的子节点(键值数的最大值+1) 来避免了这一问题。

术语叶子的定义也不一致。

  • Bayer & McCreight认为叶子层是最下面一层的键,此时这些叶子节点只包含键值,子节点指针都指向不含任何信息的空的记录。
  • Knuth 认为叶子层是最下面一层键之下的一层。如同红黑树那般(红黑树将Null指针作为黑色叶子节点),将Null作为叶子节点,此时叶子节点不存储键值等任何信息。
  • 我们一般以按Knuth大佬的说法来,清华的严蔚敏老师的数据结构一书中也是这么定义的。

内部节点:

  • 每个内部节点都至少含有 ⌈m/2⌉ 个子节点,所以至少都是半满
  • 当父节点还没满时,一个满子节点可以被分为两个合法子节点
  • 每个内部节点(除叶子节点和根节点之外的所有节点)包含以下信息
    • 指向父节点的指针Parent*
    • 键值的个数KeyNum
    • 键值Key
    • KeyNum+1个指向子节点的指针Ptr*

根节点:

  • 根节点的子节点数的最大值和内部节点一样都是m
  • 根节点的子节点数没有限制,当根节点储存的键值数小于⌈m/2⌉时,根节点可以没有子节点,此时根节点为叶子节点。

二. B树的操作

在B树中,内部(非叶子)节点可以在某个预定义范围内具有可变数量的子节点。 从节点插入或删除数据时,其子节点数会更改。 为了维持预定范围,可以将内部节点连接或拆分。

2.1. m阶B树的高度

对硬盘访问的IO次数取决于B树的高度,B树的高度如何确定呢?

首先明确的是不带任何信息的一层叶节点不算做B树的高度,通常,设一个m阶B树非叶子节点内部的关键字范围在d~2d范围内,d = ⌈m/2⌉

设h为树的高度(h≥0),空树的高度表示为0n为树中键值的总个数(n≥0),空树的总键值数为0设h为树的高度(h\ge0),空树的高度表示为0\\n为树中键值的总个数(n\ge0),空树的总键值数为0设h为树的高度(h≥0),空树的高度表示为0n为树中键值的总个数(n≥0),空树的总键值数为0

当内部关键字为2d即m时,树的高度有最小值当内部关键字为2d即m时,树的高度有最小值当内部关键字为2d即m时,树的高度有最小值
h=0时,有n0=0h=0时,有n_0=0h=0时,有n0​=0
h=1时,有n1=m−1h=1时,有n_1 = m-1h=1时,有n1​=m−1
h=2时,有n2=m(m−1)+n1=(m+1)(m−1)=m2−1h=2时,有n_2=m(m-1)+n_1=(m+1)(m-1)=m^2-1h=2时,有n2​=m(m−1)+n1​=(m+1)(m−1)=m2−1
h=3时,有n3=m2(m−1)+n2=(m2+m+1)(m−1)=m3−1h=3时,有n_3=m^2(m-1)+n_2=(m^2+m+1)(m-1)=m^3-1h=3时,有n3​=m2(m−1)+n2​=(m2+m+1)(m−1)=m3−1
…\dots…
h=h时,有n=mh−1h=h时,有n=m^{h}-1h=h时,有n=mh−1
所以hmin=logm(n+1)所以h_{min}=log_m(n+1)所以hmin​=logm​(n+1)

当内部关键字为d时即⌈m/2⌉时,树的高度有最大值当内部关键字为d时即{\lceil m/2\rceil}时,树的高度有最大值当内部关键字为d时即⌈m/2⌉时,树的高度有最大值
h=0时,n0=0h=0时,n_0=0h=0时,n0​=0
h=1时,非叶子根节点最少两个子节点和一个关键字,所以有n1=1h=1时,非叶子根节点最少两个子节点和一个关键字,所以有n_1=1h=1时,非叶子根节点最少两个子节点和一个关键字,所以有n1​=1
h=2时,有n2=2(⌈m/2⌉−1)+1=2⌈m/2⌉−1h=2时,有n_2=2({\lceil m/2\rceil}-1) + 1=2{\lceil m/2\rceil}-1h=2时,有n2​=2(⌈m/2⌉−1)+1=2⌈m/2⌉−1
h=3时,有n3=2⌈m/2⌉(⌈m/2⌉−1)+n2=2⌈m/2⌉2−1h=3时,有n_3=2{\lceil m/2\rceil}({\lceil m/2\rceil}-1) + n_2=2{\lceil m/2\rceil}^2-1h=3时,有n3​=2⌈m/2⌉(⌈m/2⌉−1)+n2​=2⌈m/2⌉2−1
…\dots…
n=2⌈m/2⌉h−1−1n=2{\lceil m/2\rceil}^{h-1}-1n=2⌈m/2⌉h−1−1
hmax=log⌈m/2⌉(n+12)+1h_{max}=log_{\lceil m/2\rceil}{(\frac{n+1}2)}+1hmax​=log⌈m/2⌉​(2n+1​)+1

看上去很复杂,其实就是等差数列求和

2.2.插入

所有的插入都从根节点开始。要插入一个新的元素,首先搜索这棵树找到新元素应该被添加到的对应节点。将新元素插入到这一节点中的步骤如下:

  • 如果节点拥有的元素数量小于最大值,那么有空间容纳新的元素。将新元素插入到这一节点,且保持节点中元素有序。
  • 否则的话这一节点已经满了,将它平均地分裂成两个节点:
    • 从该节点的原有元素和新的元素中选择出中位数,小于这一中位数的元素放入左边节点,大于这一中位数的元素放入右边节点,中位数作为分隔值。
    • 分隔值被插入到父节点中,这可能会造成父节点分裂,分裂父节点时可能又会使它的父节点分裂,以此类推。如果没有父节点(这一节点是根节点),就创建一个新的根节点(增加了树的高度)。如果分裂一直上升到根节点,那么一个新的根节点会被创建,它有一个分隔值和两个子节点。这就是根节点并不像内部节点一样有最少子节点数量限制的原因。

例子,已有4阶B树如下

插入4,根据分层查找关键字,直接插入:

插入17,根据分层查找关键字,直接插入:

插入20,子节点中关键字达到m-1,故无法继续插入,子节点分割,16分至父节点:

插入21、22,直到分裂出新的根节点

2.3.删除

删除叶子节点中的元素

  • 搜索要删除的元素
  • 如果它在叶子节点,将它从中删除
  • 如果发生了下溢出,按照后面 “删除后重新平衡”部分的描述重新调整树

删除内部节点中的元素
内部节点中的每一个元素都作为分隔两颗子树的分隔值,因此我们需要重新划分。值得注意的是左子树中最大的元素仍然小于分隔值,右子树中最小的元素仍然大于分隔值,这两个元素都在叶子节点中,并且任何一个都可以作为两颗子树的新分隔值。算法的描述如下:

  • 选择一个新的分隔符(左子树中最大的元素或右子树中最小的元素),将它从子节点中移除,替换掉被删除的元素作为新的分隔值。
  • 前一步删除了一个子节点中的元素。如果这个子节点拥有的元素数量小于最低要求,那么从这一子节点开始重新进行平衡。

删除后的重新平衡
重新平衡从叶子节点开始向根节点进行,直到树重新平衡。如果删除节点中的一个元素使该节点的元素数量低于最小值,那么一些元素必须被重新分配。通常,移动一个元素数量大于最小值的兄弟节点中的元素。如果兄弟节点都没有多余的元素,那么缺少元素的节点就必须要和他的兄弟节点合并。合并可能导致父节点失去了分隔值,所以父节点可能缺少元素并需要重新平衡。合并和重新平衡可能一直进行到根节点,根节点变成惟一缺少元素的节点。重新平衡树的递归算法如下:

  • 如果缺少元素节点的右兄弟存在且拥有多余的元素,那么向左旋转,这里的左旋转与AVL中的左旋转类似。

    • 将父节点的分隔值复制到缺少元素节点的最后(分隔值被移下来;缺少元素的节点现在有最小数量的元素)
    • 将父节点的分隔值替换为右兄弟的第一个元素(右兄弟失去了一个节点但仍然拥有最小数量的元素)
    • 树又重新平衡
  • 否则,如果缺少元素节点的左兄弟存在且拥有多余的元素,那么向右旋转
    • 将父节点的分隔值复制到缺少元素节点的第一个节点(分隔值被移下来;缺少元素的节点现在有最小数量的元素)
    • 将父节点的分隔值替换为左兄弟的最后一个元素(左兄弟失去了一个节点但仍然拥有最小数量的元素)
    • 树又重新平衡
  • 否则,如果它的两个直接兄弟节点都只有最小数量的元素,那么将它与一个直接兄弟节点以及父节点中它们的分隔值合并
    • 将分隔值复制到左边的节点(左边的节点可以是缺少元素的节点或者拥有最小数量元素的兄弟节点)
    • 将右边节点中所有的元素移动到左边节点(左边节点现在拥有最大数量的元素,右边节点为空)
    • 将父节点中的分隔值和空的右子树移除(父节点失去了一个元素)
    • 如果父节点是根节点并且没有元素了,那么释放它并且让合并之后的节点成为新的根节点(树的深度减小)
    • 否则,如果父节点的元素数量小于最小值,重新平衡父节点

先有4阶B树:

删除22,分层查询键值后,在叶子节点中直接删除:

删除3,父节点从右儿子那选择最小值(或左儿子那选择最大值),为新的键值(分隔值):

删除5,此时左兄弟只有一个键值1,没有多余键值,借不了,所以需要合并,将父节点中的分隔值4加入到左儿子节点,右儿子剩下的所有元素合并到左儿子中,合并后父节点为键值数为0,小于规定的最小键值数1(⌈m/2⌉-1),对父节点递归算法

在新一轮的算法中,对刚才失衡的父节点进行左旋转:

三. B+树的定义

3.1.B+树的定义

B+树可以视为B树的一个变种,不同的是B+树内部节点不保存数据,非叶结点中的每个引索项只含有对应子树的最大关键字和指向子树的指针,不含有该关键字对应记录的存储地址,例如关系型数据库Mysql。
一颗m阶的B+树需要满足下列条件:

  • 每个分支结点最多包含m棵子树
  • 非根结点至少有两棵子树,其他每个分支结点至少含有⌈m/2⌉\lceil m/2 \rceil⌈m/2⌉棵子树
  • 如果根节点不是叶子节点,那么它至少有两个子节点
  • 结点的子树个数与关键字个数相同,所有分支结点中仅包含它的子结点中关键字最大值以及指向该子结点的指针
  • 所有叶结点包含全部关键字以及指向相应记录的指针,叶结点中将关键字按大小顺序排列,并且相邻叶结点按大小顺序相互链接起来

3.2.B+树 VS B树

B+树相对于B树的优势有:

  • 内部节点不保存数据,占用空间小,所以每个节点可以存储的索引更多,即每个磁盘页存储的索引更多,树的高度更低,IO次数更低,磁盘查询效率更高
  • 每次查询都要到达最下面的叶子层才能拿到数据,性能更加稳定
  • 所有数据都保存在叶子节点,所有叶子节点都顺次连接,有利于遍历操作

现在你知道为什么数据库用B+树而不用B树了吧。

四. B+树的操作

4.1.插入

如果节点超过节点中键值数的规定范围则处于违规状态

  • 首先,查找要插入其中的节点的位置。接着把值插入这个节点中。
  • 如果没有节点处于违规状态则处理结束。
  • 如果某个节点有过多元素,则把它分裂为两个节点,每个都有最小数目的元素,同时将分隔值复制到父节点中作为索引。
    • 在树上递归向上继续这个处理直到到达根节点,如果根节点被分裂,则创建一个新根节点。

现有一个4阶B+树

插入2,分层遍历索引,直接插入:

插入8,分层遍历索引,节点已满,于是分裂,将新的间隔值6复制到父节点中作为索引,产生两个新的子节点:

4.2.删除删除

  • 首先,在叶子节点中查找要删除的值。接着从包含它的节点中删除这个值。
  • 如果没有节点处于违规状态则处理结束。
  • 如果节点处于违规状态则有两种可能情况:
    • 它的兄弟节点,就是同一个父节点的子节点,可以把一个或多个它的子节点转移到当前节点,而把它返回为合法状态,最后再使用从兄弟那借到的键值替代父节点中的分隔键值之后处理结束。
    • 它的兄弟节点由于处在低边界上而没有额外的子节点。在这种情况下把两个兄弟节点合并到一个单一的节点中,并删除父节点中的分隔键值
    • 如果删除后的父节点键值数量不足,进行与B树完全相同的删除平衡操作

现有一4阶B+树:

删除8,分层遍历索引,直接删除:

删除2,找左兄弟借一个数据2,用借来的键值2替代父节点中初始的索引2:

删除4,兄弟节点没有数据可以借,于是与左兄弟节点合并,删除父节点的索引4:

删除5、6,删除5时可以向右兄弟借到6,删除6时就完全借不到了,只能合并,再删除父节点中的索引

删除7,兄弟节点借不到,合并后删除父节点中的索引,父节点不符合规则,对父节点进行删除后的平衡操作,父亲节点的兄弟节点只有一个键值2,于是父节点与兄弟节点和父节点的索引3进行合并

红黑树传递门,冲冲冲

B树、B+树其实很简单,看不懂你找我相关推荐

  1. 决策树 随机森林 xgboost_从决策树到随机森林理论篇从人话到鬼话:看不懂来找我麻烦...

    从决策树产生的一些列的问题,过度到随机森林: 全文大概要阅读10分钟: 随机森林算法范畴 监督学习分类算法,基模型是由决策树组成 决策树 决策树模型与学习 特征选择 决策树生成 决策树剪枝 CART算 ...

  2. 最长上升子序列(LIS)/最长不上升子序列问题算法详解+例题(树状数组/二分优化,看不懂你来打我)

    目录 最长上升子序列 一.朴素做法O(2n)O(2^n)O(2n) 二.优化做法O(nlogn)O(nlogn)O(nlogn) 三.例题引入:P1020 导弹拦截(求最长上升子序列和最长不上升子序列 ...

  3. vue i18n 国际化保姆级教程_看不懂自己找原因

    1.国际化介绍 对于一些跨国项目来说,国际化是尤为重要的,那么什么要国际化呢?国际化的意思就是将我们写的项目,能够根据不同国家的语言,进行翻译,进行切换,方便不同国家的客户使用 基本思路如下 ① 定义 ...

  4. 漫画叙述B+树和B-树,很值得看!

    转载自:伯乐专栏作者/玻璃猫,微信公众号 - 梦见 漫画:什么是b+树 这一次我们来介绍 B+ 树. 一个m阶的B树具有如下几个特征: 1.根结点至少有两个子女. 2.每个中间节点都包含k-1个元素和 ...

  5. Codeforces Round #699 (Div. 2) E.Sorting Books(贪心+DP / 线段树)超高质量题解,看不懂来打我 ~

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 E - Sorting Books 一排书架上有 nnn 本书排成一排,每本书上有一个颜色 aia_i ...

  6. 神舟计算机硬件开发简历,【神舟电脑研发工程师面试】面试是走过场,很简单-看准网...

    首先,之前在网上查了很多相关信息,负面评论较多,但还是奔着"神舟电脑"这个老牌子去参加了宣讲会,还记得小时候总在央视看到神舟的广告,在记忆里植入了一种靠谱的形象. 11月的哈尔滨零 ...

  7. idea编辑jsp页面部分页面变黄绿色并且不提示,解决很简单,设置里边找language injection就ok...

    在编写HTML和jsp页面中出现这样的颜色,看着非常难受,并且编写代码时还没有提示,这边找了很多,最后找到了最简单的解决方法,在这分享给大家. 编写页面时不知道点到什么,就出现了这些情况,这是由于乱用 ...

  8. 红黑树,看不懂你找我

    文章目录 一:什么是红黑树 二:关于红黑树的一般操作 1.查找操作 2. 插入操作 2.1 新节点:我是红色的,我爹是红色的,我叔叔是黑色的 2.2 新节点:我是红色的,我爹是红色的,我叔叔也是红色的 ...

  9. 关于排序算法,看这一篇就够了!这篇看不懂麻烦找我拿红包

    排序算法是<数据结构与算法>中最基本的算法之一. 排序算法可以分为内部排序和外部排序. 内部排序是数据记录在内存中进行排序. 而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排 ...

最新文章

  1. 汇总|基于3D点云的深度学习方法
  2. SQL Loader 的使用详解
  3. 程序员眼中的UML(2)--克服用例图的恐惧
  4. Java面试题!Java获取异常堆栈信息
  5. 跟着书本学习CSS(2)
  6. 小明分享|ESP32-C3到底有哪些不一样的功能呢
  7. App设计灵感之十二组精美的旅行App设计案例
  8. OSSIM中配置网络资产监控
  9. 简单入门Javascript正则表达式
  10. sign check fail: check Sign and Data Fail
  11. C++(STL):25 ---序列式容器stack源码剖析
  12. 好记性不如烂笔头,记录几个常用的Linux操作
  13. 爬虫-06-通用爬虫与聚焦爬虫
  14. 推荐 | 微软SAR近邻协同过滤算法拆解(二)
  15. arch Linux安装到U盘,如何把ArchLinux安装到U盘上
  16. java三大框架要学多久_新手学习SSH三大框架的几点建议
  17. 中兴力维动环监控接线图_中兴力维动环监控与智能管理解决方案,让运维管理更高效!...
  18. js页面跳转并传递参数
  19. 面试时不能讲的跳槽理由有哪些
  20. golang的开源游戏服务器框架

热门文章

  1. Codeforces 1025D(区间dp)
  2. redis面试问题(二)
  3. 骰子的妙用---课堂答题
  4. 微信小程序--后台交互/wx.request({})方法/渲染页面方法 解析
  5. Symantec Backup Exec Remote Agent 2010在Redhat Enterprise 6.6上启动问题
  6. WPF 开发前学习(一)
  7. 使用EXE4J将JAR包转换为EXE文件
  8. 瑞友虚拟服务器网页登录,瑞友云端虚拟专网系统
  9. 设计模式复习-抽象工厂模式
  10. POJ1988(带权并查集,搬砖块)