目录树 删除 数据结构_数据结构:B树和B+树的插入、删除图文详解

B树

1.1B树的定义

B树也称B-树,它是一颗多路平衡查找树。我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数。当m取2时,就是我们常见的二叉搜索树。

一颗m阶的B树定义如下:

1)每个结点最多有m-1个关键字。

2)根结点最少可以只有1个关键字。

3)非根结点至少有Math.ceil(m/2)-1个关键字。

4)每个结点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。

5)所有叶子结点都位于同一层,或者说根结点到每个叶子结点的长度都相同。b59c11e301533d003c14f91c1a32f967.png上图是一颗阶数为4的B树。在实际应用中的B树的阶数m都非常大(通常大于100),所以即使存储大量的数据,B树的高度仍然比较小。每个结点中存储了关键字(key)和关键字对应的数据(data),以及孩子结点的指针。我们将一个key和其对应的data称为一个记录。但为了方便描述,除非特别说明,后续文中就用key来代替(key, value)键值对这个整体。在数据库中我们将B树(和B+树)作为索引结构,可以加快查询速速,此时B树中的key就表示键,而data表示了这个键对应的条目在硬盘上的逻辑地址。

1.2B树的插入操作

插入操作是指插入一条记录,即(key, value)的键值对。如果B树中已存在需要插入的键值对,则用需要插入的value替换旧的value。若B树不存在这个key,则一定是在叶子结点中进行插入操作。

1)根据要插入的key的值,找到叶子结点并插入。

2)判断当前结点key的个数是否小于等于m-1,若满足则结束,否则进行第3步。

3)以结点中间的key为中心分裂成左右两部分,然后将这个中间的key插入到父结点中,这个key的左子树指向分裂后的左半部分,这个key的右子支指向分裂后的右半部分,然后将当前结点指向父结点,继续进行第3步。

下面以5阶B树为例,介绍B树的插入操作,在5阶B树中,结点最多有4个key,最少有2个key;

a)在空树中插入39fb6b90e2df3ae71c2e8dcccbcb2307e8.png


b)继续插入22,97和4114f0b75e96b109b33efade303fae77cd.png


c)继续插入5364eea3d47b1d1be92588695166e55c82.png插入后超过了最大允许的关键字个数4,所以以key值为41为中心进行分裂,结果如下图所示,分裂后当前结点指针指向父结点,满足B树条件,插入操作结束。当阶数m为偶数时,需要分裂时就不存在排序恰好在中间的key,那么我们选择中间位置的前一个key或中间位置的后一个key为中心进行分裂即可;59b323edbb4deb8fe94691e8acb2b6a8.png


d)依次插入13,21,40,同样会造成分裂,结果如下图所示。b3c993437174cf1f7dc9f7d8d20eba70.png


e)依次插入30,27, 33 ;36,35,34 ;24,29,结果如下图所示。c571f361322e1c636351a323e02226ee.png


f)插入key值为26的记录,插入后的结果如下图所示。c459f0be739a2e0fb455d033b7c50362.png当前结点需要以27为中心分裂,并向父结点进位27,然后当前结点指向父结点,结果如下图所示。18ec5cf00c7502ec5c39287615f7bfe3.png进位后导致当前结点(即根结点)也需要分裂,分裂的结果如下图所示。b42a89065327fc6d541b06a194911de1.png分裂后当前结点指向新的根,此时无需调整。


g)最后再依次插入key为17,28,29,31,32的记录,结果如下图所示。db450e3247155231108934483842eda9.png在实现B树的代码中,为了使代码编写更加容易,我们可以将结点中存储记录的数组长度定义为m而非m-1,这样方便底层的结点由于分裂向上层插入一个记录时,上层有多余的位置存储这个记录。同时,每个结点还可以存储它的父结点的引用,这样就不必编写递归程序。

一般来说,对于确定的m和确定类型的记录,结点大小是固定的,无论它实际存储了多少个记录。但是分配固定结点大小的方法会存在浪费的情况,比如key为28,29所在的结点,还有2个key的位置没有使用,但是已经不可能继续在插入任何值了,因为这个结点的前序key是27,后继key是30,所有整数值都用完了。所以如果记录先按key的大小排好序,再插入到B树中,结点的使用率就会很低,最差情况下使用率仅为50%。

1.3B树的删除操作

删除操作是指,根据key删除记录,如果B树中的记录中不存对应key的记录,则删除失败。

1)如果当前需要删除的key位于非叶子结点上,则用后继key(这里的后继key均指后继记录的意思)覆盖要删除的key,然后在后继key所在的子支中删除该后继key。此时后继key一定位于叶子结点上,这个过程和二叉搜索树删除结点的方式类似。删除这个记录后执行第2步

2)该结点key个数大于等于Math.ceil(m/2)-1,结束删除操作,否则执行第3步。

3)如果兄弟结点key个数大于Math.ceil(m/2)-1,则父结点中的key下移到该结点,兄弟结点中的一个key上移,删除操作结束。

否则,将父结点中的key下移与当前结点及它的兄弟结点中的key合并,形成一个新的结点。原父结点中的key的两个孩子指针就变成了一个孩子指针,指向这个新结点。然后当前结点的指针指向父结点,重复上第2步。

有些结点它可能即有左兄弟,又有右兄弟,那么我们任意选择一个兄弟结点进行操作即可。

下面以5阶B树为例,介绍B树的删除操作,5阶B树中,结点最多有4个key,最少有2个key

a)原始状态487f5691fe825f54e1bc6ebd449affc5.png


b)在上面的B树中删除21,删除后结点中的关键字个数仍然大于等2,所以删除结束。7b1a8b9fc6b668fdd8a49da155f3ceb1.png


c)在上述情况下接着删除27。从上图可知27位于非叶子结点中,所以用27的后继替换它。从图中可以看出,27的后继为28,我们用28替换27,然后在28(原27)的右孩子结点中删除28。删除后的结果如下图所示。689e838d969a7db35469bd1c4f0f2081.png删除后发现,当前叶子结点的记录的个数小于2,而它的兄弟结点中有3个记录(当前结点还有一个右兄弟,选择右兄弟就会出现合并结点的情况,不论选哪一个都行,只是最后B树的形态会不一样而已),我们可以从兄弟结点中借取一个key。所以父结点中的28下移,兄弟结点中的26上移,删除结束。结果如下图所示。acb3e2fc24c14474d5aae75d359bf8fb.png


d)在上述情况下接着32,结果如下图。28531b990ece35ca0c3b8e92c856725f.png当删除后,当前结点中只key,而兄弟结点中也仅有2个key。所以只能让父结点中的30下移和这个两个孩子结点中的key合并,成为一个新的结点,当前结点的指针指向父结点。结果如下图所示。007e1bb498ece6862138bd072e9f2559.png当前结点key的个数满足条件,故删除结束。


e)上述情况下,我们接着删除key为40的记录,删除后结果如下图所示。a49e67f5e21554e301bd2f2b4e9c515c.png同理,当前结点的记录数小于2,兄弟结点中没有多余key,所以父结点中的key下移,和兄弟(这里我们选择左兄弟,选择右兄弟也可以)结点合并,合并后的指向当前结点的指针就指向了父结点。a36dd1ff9fb90cdc322323965746274c.png同理,对于当前结点而言只能继续合并了,最后结果如下所示。1a33e443888e5f30a5a8492420c3a061.png合并后结点当前结点满足条件,删除结束。

B+树

2.1 B+树的定义

f3a5d170ff513ed2b9176425adbced08.png各种资料上B+树的定义各有不同,一种定义方式是关键字个数和孩子结点个数相同。这里我们采取维基百科上所定义的方式,即关键字个数比孩子结点个数小1,这种方式是和B树基本等价的。上图就是一颗阶数为4的B+树。

除此之外B+树还有以下的要求。

1)B+树包含2种类型的结点:内部结点(也称索引结点)和叶子结点。根结点本身即可以是内部结点,也可以是叶子结点。根结点的关键字个数最少可以只有1个。

2)==B+树与B树最大的不同是内部结点不保存数据,只用于索引,所有数据(或者说记录)都保存在叶子结点中;==

3) m阶B+树表示了内部结点最多有m-1个关键字(或者说内部结点最多有m个子树),即有n个关键字就有n个子数;阶数m同时限制了叶子结点最多存储m-1个记录;

4)内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。

5)每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接。

2.2 B+树的插入操作

1)若为空树,创建一个叶子结点,然后将记录插入其中,此时这个叶子结点也是根结点,插入操作结束。

2)针对叶子类型结点:根据key值找到叶子结点,向这个叶子结点插入记录。插入后,若当前结点key的个数小于等于m-1,则插入结束。否则将这个叶子结点分裂成左右两个叶子结点,左叶子结点包含前m/2个记录,右结点包含剩下的记录,将第m/2+1个记录的key进位到父结点中(父结点一定是索引类型结点),进位到父结点的key左孩子指针向左结点,右孩子指针向右结点。将当前结点的指针指向父结点,然后执行第3步。

3)针对索引类型结点:若当前结点key的个数小于等于m-1,则插入结束。否则,将这个索引类型结点分裂成两个索引结点,左索引结点包含前(m-1)/2个key,右结点包含m-(m-1)/2个key,将第m/2个key进位到父结点中,进位到父结点的key左孩子指向左结点, 进位到父结点的key右孩子指向右结点。将当前结点的指针指向父结点,然后重复第3步。

下面是一颗5阶B树的插入过程,5阶B数的结点最少2个key,最多4个key。

a)空树中插入55550b15be6b92fcdbb6e5c66d837299a.png


b)依次插入8,10,154baea31772093481bf8e707a71037855.png


c)插入16cec7ef2886f5e21dd8fcb625c1de623e.png插入16后超过了关键字的个数限制,所以要进行分裂。在叶子结点分裂时,分裂出来的左结点2个记录,右边3个记录,中间key成为索引结点中的key,分裂后当前结点指向了父结点(根结点)。结果如下图所示。402239e18746ff3d407b333b600f923a.png当然我们还有另一种分裂方式,给左结点3个记录,右结点2个记录,此时索引结点中的key就变为15。


d)插入176a4332aed39f9ed330e4e7addf7dd9ae.png


e)插入18,插入后如下图所示2fc9fc1d7c9bb48f4b004aed24b7ca2c.png当前结点的关键字个数大于5,进行分裂。分裂成两个结点,左结点2个记录,右结点3个记录,关键字16进位到父结点(索引类型)中,将当前结点的指针指向父结点。44a8fd44ce53cba95926fff620415e0d.png当前结点的关键字个数满足条件,插入结束。


f)插入若干数据后dc858a2090eef6811a570ff0210cefdf.png


g)在上图中插入7,结果如下图所示8e9988b1aa8868295414d6ca71f41353.png当前结点的关键字个数超过4,需要分裂。左结点2个记录,右结点3个记录。分裂后关键字7进入到父结点中,将当前结点的指针指向父结点,结果如下图所示。5c69603bfb718556f289dde20f1cf1d6.png当前结点的关键字个数超过4,需要继续分裂。左结点2个关键字,右结点2个关键字,关键字16进入到父结点中,将当前结点指向父结点,结果如下图所示。

5d4ee0d466deb38e879cc7c9fe6a94d1.png当前结点的关键字个数满足条件,插入结束。

2.3 B+树的删除操作

如果叶子结点中没有相应的key,则删除失败。否则执行下面的步骤

1)删除叶子结点中对应的key。删除后若结点的key的个数大于等于Math.ceil(m-1)/2 – 1,删除操作结束,否则执行第2步。

2)若兄弟结点key有富余(大于Math.ceil(m-1)/2 – 1),向兄弟结点借一个记录,同时用借到的key替换父结(指当前结点和兄弟结点共同的父结点)点中的key,删除结束。否则执行第3步。

3)若兄弟结点中没有富余的key,则当前结点和兄弟结点合并成一个新的叶子结点,并删除父结点中的key(父结点中的这个key两边的孩子指针就变成了一个指针,正好指向这个新的叶子结点),将当前结点指向父结点(必为索引结点),执行第4步(第4步以后的操作和B树就完全一样了,主要是为了更新索引结点)。

4)若索引结点的key的个数大于等于Math.ceil(m-1)/2 – 1,则删除操作结束。否则执行第5步

5)若兄弟结点有富余,父结点key下移,兄弟结点key上移,删除结束。否则执行第6步

6)当前结点和兄弟结点及父结点下移key合并成一个新的结点。将当前结点指向父结点,重复第4步。

注意,通过B+树的删除操作后,索引结点中存在的key,不一定在叶子结点中存在对应的记录。

下面是一颗5阶B树的删除过程,5阶B数的结点最少2个key,最多4个key。

a)初始状态96918a1ee3306906bf658239c2a0b1ab.png


b)删除22,删除后结果如下图1ab49f1423f4338698ca9dfa8d36bf98.png删除后叶子结点中key的个数大于等于2,删除结束


c)删除15,删除后的结果如下图所示65919389d946953ef45f1b8ddc3b85fa.png删除后当前结点只有一个key,不满足条件,而兄弟结点有三个key,可以从兄弟结点借一个关键字为9的记录,同时更新将父结点中的关键字由10也变为9,删除结束。69e9e3b7b327edea2307d31554ba6d1b.png


d)删除7,删除后的结果如下图所示d8298960758a5e4dea3b69f292230d64.png当前结点关键字个数小于2,(左)兄弟结点中的也没有富余的关键字(当前结点还有个右兄弟,不过选择任意一个进行分析就可以了,这里我们选择了左边的),所以当前结点和兄弟结点合并,并删除父结点中的key,当前结点指向父结点。cec635ac3c09215a450168c5a48b1fad.png此时当前结点的关键字个数小于2,兄弟结点的关键字也没有富余,所以父结点中的关键字下移,和两个孩子结点合并,结果如下图所示。c844fb2ee3a30966757aee7931d03c15.png声明: 转载自:B树和B+树的插入、删除图文详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468833.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MMdetection框架速成系列 第01部分:学习路线图与步骤+优先学习的两个目标检测模型代码+loss计算流程+遇到问题如何求助+Anaconda3下的安装教程(mmdet+mmdet3d)

mmdetection 学习目录1 mmdetection 学习建议1.1 mmdetection 学习的第一件事1.2 mmdetection学习路线图1.2.1 优先看的两个库1.2.2 阅读代码1.2.3 代码学习步骤1.2.4 建议优先学习的两个目标检测模型代码1.2.5 loss计算流程的攻坚克难1.3 遇到问题如何求助2 Anaconda3下的安装…

机器人循迹小车资料

前言 我记得在大学的时候,参加电子比赛,我们有一个题目是平衡小车项目,那个对基础要求还是比较高的,总结了一些平衡车机器的资料,希望对大家有帮助。 正文 关注公众号,回复【机器人资料】获取

单片机实现环形队列_稀疏数组和队列(二)

队列的介绍队列以一种先入先出(FIFO)的线性表,还有一种先入后出的线性表(FILO)叫做栈。教科书上有明确的定义与描述。类似于现实中排队时的队列(队尾进,队头出),队列只在线性表两端进行操作,插入元素的一端称为表尾,删…

漫画-Linux中断子系统综述

1、中断引发的面试教训2、什么是中断?中断: (英语:Interrupt)指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。即在程序运行过程中,系统出现了一个必须由CPU立即…

SQL强化(二) 在Oracle 中写代码

一 : 关于查询中的转换 -- 字符串转换 一 : decode 函数 转换 SELECT DECODE ( PROTYPE.PRO_TYPE_DATE, L, 长, m, 短, 默认值 )FROM PROTYPE -- 字符串转换 二 : case 转换 SELECT T1.PRO_TYPE_ID, T1.PRO_TYPE_NAME, CASE T1.PRO_TYPE_DATEWHEN S THEN 短WHEN L THEN 长EL…

vue 非es6 写法怎么按须加载_Vue源码必学指南:flow(语法检查)以及rollup(模板打包)...

点击上方蓝色字关注我们~一、前言虽然 Vue3 已经公开了代码,但是Vue3.0还处于开发阶段,直接上手使用Typescript是不合适的 , 对于前端的老手是不错的选择, 但是如果没有研究源码经验的开发者还是建议使用完善, 成熟的源码进行入手. 而 Vue 2.x 中使用的 flow 是一个…

漫画|创业到底有多难?

我有一群同事,我们一起经历了一个产品从无到有的过程,从开始的斗志满满到最后跟老板的不欢而散,其中的辛酸苦楚也许只能我们自己能体味,在这过程中,我们共同经历过的事情,有快乐的,悲伤的&#…

生成step文件_利用opencv给彦女王生成一副蒙太奇画像

大家好呀,前两天烈阳天道1上映了,不知道大家看没看呢,里面还有一小段彦穿越虫洞与猴哥相遇的画面,彦女王啊啊啊~~所以我去网上爬了二百来张我大学的风景画,然后找了以前存的彦女王的图片,生成了一幅蒙太奇画…

浪漫情人节|C语言画心型

1.前言新年第一天上班,先祝大家新年快乐,巧的是,今天刚好又是情人节,所以想了下用C实现画心形符号~过年的时候,跟我表哥去接新娘,实地看了下,如果一个汉字内心没有点浪漫的细胞,很难…

CS190.1x Scalable Machine Learning

这门课是CS100.1x的后续课,看课程名字就知道这门课主要讲机器学习。难度也会比上一门课大一点。如果你对这门课感兴趣,可以看看我这篇博客,如果对PySpark感兴趣,可以看我分析作业的博客。 Course Software Setup 这门课的环境配置…

./4.sh: No such file or directory

sh push到目标板后提示出错 #!/bin/bash echo "ladjfaosdjfoia"头bin/bash 我们要看,sh在哪里 130|rk3399_idpad:/data # which sh /system/bin/sh rk3399_idpad:/data # 所以上面的代码应该写成 #!/system/bin/sh echo "ladjfaosdjfoia"修…

Spring总结四:IOC和DI 注解方式

首先我们要了解注解和xml配置的区别: 作用一样,但是注解写在Bean的上方来代替我们之前在xml文件中所做的bean配置,也就是说我们使用了注解的方式,就不用再xml里面进行配置了,相对来说注解方式更为简便。 IOC获取对象注…

和后台如何对接_业务系统如何对接第三方服务?

在产品工作中,我们时常要对接第三方服务。本文作者从过往的对接项目经历中,提炼的关于业务系统,如何对接第三方服务的方法论,希望能对你有所帮助。随着公司业务的发展,我们有时会遇到,需要在自身业务系统中…

adb 启动某个apk

有时候需要用apk来启动某个apk adb shell am start -n com.android.launcher3/com.android.launcher3.Launcher 具体查看~ /rk3399_7in1/packages/apps/Launcher3/AndroidManifest.xml

Makefile 文件中的:obj-$(CONFIG_TEST) += test.o,这一类的是什么意思?

1、obj-$ $(CONFIG_TEST) 是一个整体,$(bbb)表示引用变量 bbb 比如定义 CONFIG_TESTy $(CONFIG_TEST) 就是 y obj-$(CONFIG_TEST) 就是 obj-y 又比如定义 CONFIG_TESTm $(CONFIG_TEST) 就是 m obj-$(CONFIG_TEST) 就是 obj-m obj-y foo.o 该例子告诉Kbuild在这目…

Kconfig中的“depends on”和“select”

在Kconfig文件中: config Adepends on Bselect C它的含义是:CONFIG_A配置与否,取决于CONFIG_B是否配置。一旦CONFIG_A配置了,CONFIG_C也自动配置了。 参考资料:“select” vs “depends” in kernel Kconfig。 所以去…

数组的合并和升序排列_leetcode 33 搜索旋转排序数组

给你一个升序排列的整数数组 nums ,和一个整数 target 。假设按照升序排序的数组在预先未知的某个点上进行了旋转。(例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请你在数组中搜索 target ,如果数组中存在…

[LeetCode] [C++] 206 Reverse Linked List 反转单项链表

题目要求 Reverse a singly linked list.LeetCode 206在线测试 问题描述 给定一个单项链表,将其反转后返回链表头节点。 思路分析1 可以完整的遍历一遍链表,将链表的每个节点的值存在数组中,然后反向遍历数组重新生存一个新 链表。这样做需要…

qq面板(仿版,未完待续中。。。。)---2017-04-24

主要实现效果: 1、点击对话,显示对话;点击联系人,显示联系人 2、在联系人界面: 实现好友列表的展开与折叠;(图12) 实现鼠标移到好友列表上的背景颜色的变化;(…

苹果企业证书_苹果签名经常掉签原因大汇总

苹果签名就是数字签名,是基于非对称加密算法来实现的,对称加密就是通过非对称加密算法实现的,对称加密是通过同一份秘钥加密解密数据,非对称加密有两份秘钥,分别是公钥和私钥,用公钥进行加密的数据只能使用…