博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解平衡二叉树,B树,B+树
阅读量:2354 次
发布时间:2019-05-10

本文共 4607 字,大约阅读时间需要 15 分钟。

1、平衡二叉树

概念

平衡二叉树是基于二分法的策略提高数据的查找速度的二叉树的数据结构;

特点:

平衡二叉树是采用二分法思维把数据按规则组装成一个树形结构的数据,用这个树形结构的数据减少无关数据的检索,大大的提升了数据检索的速度;平衡二叉树的数据结构组装过程有以下规则:

(1)非叶子节点只能允许最多两个子节点存在。

(2)每一个非叶子节点数据分布规则为左边的子节点小当前节点的值,右边的子节点大于当前节点的值(这里值是基于自己的算法规则而定的,比如hash值);
在这里插入图片描述
平衡树的层级结构:因为平衡二叉树查询性能和树的层级(h高度)成反比,h值越小查询越快、为了保证树的结构左右两端数据大致平衡降低二叉树的查询难度一般会采用一种算法机制实现节点数据结构的平衡,实现了这种算法的有比如Treap、红黑树,使用平衡二叉树能保证数据的左右两边的节点层级相差不会大于1.,通过这样避免树形结构由于删除增加变成线性链表影响查询效率,保证数据平衡的情况下查找数据的速度近于二分法查找;
在这里插入图片描述
平衡二叉树特点:

(1)非叶子节点最多拥有两个子节点;

(2)非叶子节值大于左边子节点、小于右边子节点;
(3)树的左右两边的层级数相差不会大于1;
(4)没有值相等重复的节点;

2、B树(B-tree)

概念

B树是一种自平衡树数据结构,它维护有序数据并允许以对数时间进行搜索,顺序访问,插入和删除。B树是二叉搜索树的一般化,因为节点可以有两个以上的子节点。[1]与其他自平衡二进制搜索树不同,B树非常适合读取和写入相对较大的数据块(如光盘)的存储系统。它通常用于数据库和文件系统。

  
特点

(1)每个节点最多只有m个子节点。

(2)每个非叶子节点(除了根)具有至少⌈ m/2⌉子节点。
(3)如果根不是叶节点,则根至少有两个子节点。
(4)具有k个子节点的非叶节点包含k -1个键。
(5)所有叶子都出现在同一水平,没有任何信息(高度一致)。

B树的阶:B树中一个节点的子节点数目的最大值,用m表示,假如最大值为10,则为10阶,如图

在这里插入图片描述
所有节点中,节点【13,16,19】拥有的子节点数目最多,四个子节点(灰色节点),所以可以定义上面的图片为4阶B树。

根节点:节点【10】即为根节点,特征:根节点拥有的子节点数量的上限和内部节点相同,如果根节点不是树中唯一节点的话,至少有俩个子节点(不然就变成单支了)。在m阶B树中(根节点非树中唯一节点),那么有关系式2<= M <=m,M为子节点数量;包含的元素数量 1<= K <=m-1,K为元素数量。

内部节点:节点【13,16,19】、节点【3,6】都为内部节点,特征:内部节点是除叶子节点和根节点之外的所有节点,拥有父节点和子节点。假定m阶B树的内部节点的子节点数量为M,则一定要符合(m/2)<= M <=m关系式,包含元素数量M-1;包含的元素数量 (m/2)-1<= K <=m-1,K为元素数量。m/2向上取整。

叶子节点:节点【1,2】、节点【11,12】等最后一层都为叶子节点,叶子节点对元素的数量有相同的限制,但是没有子节点,也没有指向子节点的指针。特征:在m阶B树中叶子节点的元素符合(m/2)-1<= K <=m-1

插入

针对m阶高度h的B树,插入一个元素时,首先在B树中是否存在,如果不存在,即在叶子结点处结束,然后在叶子结点中插入该新的元素。

若该节点元素个数小于m-1,直接插入;

若该节点元素个数等于m-1,引起节点分裂;以该节点中间元素为分界,取中间元素(偶数个数,中间两个随机选取)插入到父节点中;
重复上面动作,直到所有节点符合B树的规则;最坏的情况一直分裂到根节点,生成新的根节点,高度增加1;
上面三段话为插入动作的核心,接下来以5阶B树为例,详细讲解插入的动作;

5阶B树关键点:

2<=根节点子节点个数<=5

3<=内节点子节点个数<=5
1<=根节点元素个数<=4
2<=非根节点元素个数<=4
在这里插入图片描述在这里插入图片描述
图(1)插入元素【8】后变为图(2),此时根节点元素个数为5,不符合 1<=根节点元素个数<=4,进行分裂(真实情况是先分裂,然后插入元素,这里是为了直观而先插入元素,下面的操作都一样,不再赘述),取节点中间元素【7】,加入到父节点,左右分裂为2个节点,如图(3)
在这里插入图片描述
接着插入元素【5】,【11】,【17】时,不需要任何分裂操作,如图(4)
在这里插入图片描述
插入元素【13】
在这里插入图片描述

节点元素超出最大数量,进行分裂,提取中间元素【13】,插入到父节点当中,如图(6)

在这里插入图片描述

接着插入元素【6】,【12】,【20】,【23】时,不需要任何分裂操作,如图(7)

在这里插入图片描述

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

在这里插入图片描述

插入【4】时,导致最左边的叶子结点被分裂,【4】恰好也是中间元素,上移到父节点中,然后元素【16】,【18】,【24】,【25】陆续插入不需要任何分裂操作

在这里插入图片描述

最后,当插入【19】时,含有【14】,【16】,【17】,【18】的结点需要分裂,把中间元素【17】上移到父节点中,但是情况来了,父节点中空间已经满了,所以也要进行分裂,将父节点中的中间元素【13】上移到新形成的根结点中,这样具体插入操作的完成。

在这里插入图片描述

删除

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

某结点中元素数目小于(m/2)-1,(m/2)向上取整,则需要看其某相邻兄弟结点是否丰满;

如果丰满(结点中元素个数大于(m/2)-1),则向父节点借一个元素来满足条件;
如果其相邻兄弟都不丰满,即其结点数目等于(m/2)-1,则该结点与其相邻的某一兄弟结点进行“合并”成一个结点;
接下来还以5阶B树为例,详细讲解删除的动作;

关键要领,元素个数小于 2(m/2 -1)就合并,大于4(m-1)就分裂

如图依次删除依次删除【8】,【20】,【18】,【5】
在这里插入图片描述

首先删除元素【8】,当然首先查找【8】,【8】在一个叶子结点中,删除后该叶子结点元素个数为2,符合B树规则,操作很简单,咱们只需要移动【11】至原来【8】的位置,移动【12】至【11】的位置(也就是结点中删除元素后面的元素向前移动)

在这里插入图片描述

下一步,删除【20】,因为【20】没有在叶子结点中,而是在中间结点中找到,咱们发现他的继承者【23】(字母升序的下个元素),将【23】上移到【20】的位置,然后将孩子结点中的【23】进行删除,这里恰好删除后,该孩子结点中元素个数大于2,无需进行合并操作。

在这里插入图片描述

下一步删除【18】,【18】在叶子结点中,但是该结点中元素数目为2,删除导致只有1个元素,已经小于最小元素数目2,而由前面我们已经知道:如果其某个相邻兄弟结点中比较丰满(元素个数大于ceil(5/2)-1=2),则可以向父结点借一个元素,然后将最丰满的相邻兄弟结点中上移最后或最前一个元素到父节点中,在这个实例中,右相邻兄弟结点中比较丰满(3个元素大于2),所以先向父节点借一个元素【23】下移到该叶子结点中,代替原来【19】的位置,【19】前移;然【24】在相邻右兄弟结点中上移到父结点中,最后在相邻右兄弟结点中删除【24】,后面元素前移。在这里插入图片描述

最后一步删除【5】, 删除后会导致很多问题,因为【5】所在的结点数目刚好达标,刚好满足最小元素个数(ceil(5/2)-1=2),而相邻的兄弟结点也是同样的情况,删除一个元素都不能满足条件,所以需要该节点与某相邻兄弟结点进行合并操作;首先移动父结点中的元素(该元素在两个需要合并的两个结点元素之间)下移到其子结点中,然后将这两个结点进行合并成一个结点。所以在该实例中,咱们首先将父节点中的元素【4】下移到已经删除【5】而只有【6】的结点中,然后将含有【4】和【6】的结点和含有【1】,【3】的相邻兄弟结点进行合并成一个结点。在这里插入图片描述

也许你认为这样删除操作已经结束了,其实不然,在看看上图,对于这种特殊情况,你立即会发现父节点只包含一个元素【7】,没达标(因为非根节点包括叶子结点的元素K必须满足于2=<K<=4,而此处的K=1),这是不能够接受的。如果这个问题结点的相邻兄弟比较丰满,则可以向父结点借一个元素。而此时兄弟节点元素刚好为2,刚刚满足,只能进行合并,而根结点中的唯一元素【13】下移到子结点,这样,树的高度减少一层。

在这里插入图片描述

3、B+树

特点

(1)有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。

(2)所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
(3)所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
在这里插入图片描述
在上面这棵树中,根节点元素8是子节点2,5,8的最大元素,需要注意的是,根节点的最大元素(这里是15),也就等同于整个B+树的最大元素。以后无论插入删除多少元素,始终要保持最大元素在根节点当中。至于叶节点中,由于
父节点的元素都出现在子节点,因此所有叶子结点包含了全部元素信息。并且每一个叶子节点都带有指向下一个节点的指针,形成了一个有序链表
在这里插入图片描述
B+树还有一个特点,这个特别是在索引之外,确是至关重要的特点。那就是卫星数据的位置。所谓卫星数据,指的是索引元素所指向的数据记录,比如数据库中的某一行。在B-树中,无论中间节点还是叶子节点都带有卫星数据。而在B+中,只有叶子节点带有卫星数据,其余中间节点仅仅是索引,没有任何数据关联。

B-树中的卫星数据:

在这里插入图片描述
B+树中的卫星数据:
在这里插入图片描述
需要补充的是,在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。

由于B+数中间节点没有卫星数据,所以同样大小的磁盘页可以容纳更多的节点元素。这就意味着,数据量相同的情况下,B+树的结构更加“矮胖”,因此查询时IO次数也更少。其次,B+书的查询必须最终查找到叶子节点,而B-树只要找到匹配元素即可,无论匹配元素处于中间节点还是叶子节点。因此,B-树的查找性能并不稳定(最好情况是只查根节点,最坏是查到叶子点),而B+树的每一次查找都是稳定的。B-树只能依靠繁琐的中序遍历做范围查询,比如查询3到11的元素,而B+树的范围查询只需在链表上做遍历即可。

B-树的范围查找过程

自顶向下,查找到范围的下限(3):

在这里插入图片描述
中序遍历到元素6:
在这里插入图片描述
中序遍历到元素8:
在这里插入图片描述
中序遍历到元素9:
在这里插入图片描述
中序遍历到元素11,遍历结束:
在这里插入图片描述
B+树的范围查找过程

自顶向下,查找到范围的下限(3):

在这里插入图片描述
通过链表指针,遍历到元素6, 8:
在这里插入图片描述
通过链表指针,遍历到元素9, 11,遍历结束:
在这里插入图片描述
总结B+树的优势:

(1)单一节点存储更多的元素,使得查询的IO次数更少。

(2)所有查询都要查找到叶子节点,查询性能稳定。
(3)所有叶子节点形成有序链表,便于范围查询。

转载地址:http://iiwtb.baihongyu.com/

你可能感兴趣的文章
ZooKeeper 数据与存储配置
查看>>
ZooKeeper 安装部署
查看>>
ZooKeeper 配置
查看>>
11.组合模式--Composite
查看>>
12.轻量模式--Flyweight
查看>>
13.外观模式--Facade
查看>>
开源史上最成功的八个开源软件
查看>>
More Effective C++读书笔记
查看>>
关于assert,ASSERT,TRACE和VERIFY
查看>>
关于C++中野指针的说明
查看>>
Linux/Unix环境下的make和makefile详解
查看>>
SourceInsight添加对汇编语言文件.s和.S的支持
查看>>
windows 下实现函数打桩:拦截API方式
查看>>
获取Windows系统版本
查看>>
漫谈兼容内核之十二:Windows的APC机制
查看>>
21.windbg-.lastevent、!analyze(dump分析、异常错误码查询)
查看>>
16.windbg-.frame、dt(切换局部上下文、查找结构体)
查看>>
开源任务管理器 Process Hacker (Windows)
查看>>
快速发现Windows中毒的工具:Process Hacker
查看>>
Process Hacker源码中的用户态hook的做法
查看>>