B树和B+树的插入、删除
1. B树
1.1 B树的定义
树也称
树,它是一颗多路平衡查找树。我们描述一颗
树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,用字母
表示阶数。当
取
时,就是我们常见的二叉搜索树。
一颗阶的
树定义如下:
- 每个结点最多有
个关键字。
- 根结点最少可以只有
个关键字。
- 非根结点至少有
个关键字。
- 每个结点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于该结点,而右子树中的所有关键字都大于该结点。
- 所有叶子结点都位于同一层,或者说根结点到每个叶子结点的长度都相同。
上图是一颗阶数为的
树。在实际应用中的
树的阶数
都非常大(通常大于
),所以即使存储大量的数据,
树的高度仍然比较小。每个结点中存储了关键字
和关键字对应的数据
,以及孩子结点的指针。我们将一个
和其对应的
称为一个记录。为方便描述,除非特别说明,后续文中就用
来代替
键值对这个整体。在数据库中我们将
树(和
树)作为索引结构,可以加快查询速速,此时
树中的
就表示键,而
表示了这个键对应的条目在硬盘上的逻辑地址。
1.2 B树的插入操作
插入操作是指插入一条记录,即的键值对。如果
树中已存在需要插入的键值对,则用需要插入的
替换旧
。若
树不存在该
,则一定是在叶子结点中进行插入操作。
- 根据要插入的
的值,找到叶子结点并插入;
- 判断当前结点
的个数是否小于等于
,若满足则结束,否则进行第3步;
- 以结点中间的
为中心分裂成左右两部分,将这个中间的
插入到父结点中,让这个
的左侧指针指向分裂后的左半部分,
的右侧指针指向分裂后的右半部分,然后设置当前结点指向父结点(也就是
此时所在的结点),继续进行第3步;
- 下面以
阶
树为例,介绍
树的插入操作,结点最多有
个
,最少有
个
。
a)在空树中插入
此时根结点就一个,此时根结点也是叶子结点
b)继续插入,
和
根结点此时有个
c)继续插入
插入后超过了最大允许的关键字个数,以
值为
为中心进行分裂,结果如下图所示,分裂后当前结点指针指向父结点,满足
树条件,插入操作结束。当阶数
为偶数时,需要分裂时就不存在排序恰好在中间的
,那么选择中间位置的前一个
或后一个
为中心进行分裂即可。
d)依次插入,
,
,同样会造成分裂,结果如下图所示。
e)依次插入,
,
;
,
,
;
,
,结果如下图所示。
f)插入值为
的记录,插入后的结果如下图所示。
当前结点需要以为中心分裂,并向父结点插入
,然后当前结点指向父结点,结果如下图示。
父(当前即根结点)结点插入后导致父结点也需要分裂,分裂的结果如下图所示。
分裂后当前结点指向新的根,此时无需调整。
g)最后再依次插入为
,
,
,
,
的记录,结果如下图所示。
在实现树的代码中,为了使代码编写更加容易,我们可以将结点中存储记录的数组长度定义为
而非
,这样方便底层的结点由于分裂向上插入一个记录时,上层有多余位置暂存这个记录。同时,每个结点还可以存储它的父结点的引用,这样就不必编写递归程序。
一般来说,对于确定的和确定类型的记录,结点大小是固定的,无论它实际存储了多少个记录。但是分配固定结点大小的方法会存在浪费的情况,比如
为
,
所在的结点,还有
个
的位置没有使用,但已不可能再在此插入任何值了,因为这个结点的前序
是27,后继
是
,所有整数值都用完了。
1.3 B树的删除操作
删除操作是指,根据删除记录,如果
树中的记录中不存对应
的记录,则删除失败。
- 如果当前需要删除的
位于非叶子结点上,则用后继
(这里的后继
均指后继记录的意思)覆盖要删除的
,然后在后继
所在的子支中删除该后继key。后继
一定位于叶子结点上,这个过程和二叉搜索树删除结点的方式类似。删除这个记录后执行第2步;
- 该结点
个数大于等于
,结束删除操作,否则执行第3步;
- 如果兄弟结点
个数大于
,则在父结点与兄弟结点夹住的
下移到当前结点,兄弟结点中的一个
上移到父结点,删除操作结束;
- 否则,将父结点中的
下移与当前结点及它的兄弟结点中的
合并,形成一个新的结点。原父结点中的
的两个孩子指针就变成了一个孩子指针,指向这个新结点。然后当前结点的指针指向父结点,重复上第2步。
有些结点它可能即有左兄弟,又有右兄弟,那么我们任意选择一个兄弟结点进行操作即可。
下面以阶
树为例,介绍
树的删除操作,
阶
树中,结点最多有
个
,最少有
个
a)原始状态
b)在上面的树中删除
,删除后结点中的关键字个数仍然大于等
,所以删除结束。
c)在上述情况下接着删除。从上图可知
位于非叶子结点中,所以用
的后继替换它。图中可以看出,
的后继为
,我们用
替换
,然后在
(原
)的右孩子结点中删除
。删除后的结果如下图所示。
删除后发现,当前叶子结点的记录的个数小于,而它的兄弟结点中有
个记录(当前结点还有一个右兄弟,选择右兄弟就会出现合并结点的情况,不论选哪一个都行,只是最后
树的形态会不一样而已),我们可以从兄弟结点中借取一个
。所以父结点中的
下移,兄弟结点中的
上移,删除结束。结果如下图所示。
d)在上述情况下接着,结果如下图。
当删除后,当前结点中只,而兄弟结点中也仅有
个
。只能让父结点中的
下移和这两个孩子结点中的
合并,成为一个新的结点,当前结点的指针指向父结点。结果如下图所示。
当前结点的个数满足条件,故删除结束。
e)上述情况下,我们接着删除为
的记录,删除后结果如下图所示。
同理,当前结点的记录数小于,兄弟结点中没有多余
,所以父结点中的
下移,和兄弟(这里我们选择左兄弟,选择右兄弟也可以)结点合并,合并后的指向当前结点指针指向父结点。
同理,对于当前结点而言只能继续合并了,最后结果如下所示。
合并后结点当前结点满足条件,删除结束。
B+树
2.1 B+树的定义
各种资料上树的定义各有不同,一种定义方式是关键字个数和孩子结点数相同。这里我们采取维基百科上所定义的方式,即关键字个数比孩子结点个数小
,这种方式是和B树基本等价的。上图就是一颗阶数为
的
树。
除此之外树还有以下的要求。
树包含
种类型结点:内部结点(也称索引结点)和叶子结点。根结点本身既可以是内部结点,也可以是叶子结点。根结点的
个数最少可以只有
个;
树与
树最大的不同是内部结点不保存数据,只用于索引,所有的数据(或者说记录)都保存在叶子结点中;
阶
树表示了内部结点最多有
个
(或者说内部结点最多有
个子树),阶数
同时限制了叶子结点最多存储
个记录;
- 内部结点中的
都按照从小到大的顺序排列,对于内部结点中的一个
,左树中的所有
都小于它,右子树中的
都大于等于它。叶子结点中记录也按照
大小排列;
- 每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依
自小而大顺序链接。
2.2 B+树的插入操作
- 若为空树,创建一个叶子结点,然后将记录插入其中,此时这个叶子结点也是根结点,插入操作结束;
- 根据
值找到叶子结点,向这个叶子结点插入记录。插入后,当前结点
的个数小于等于
,则插入结束。否则将这个叶子结点分裂成左右两个叶子结点,左叶子结点包含前
个记录,右结点包含剩下记录,将第
个记录的
进位到父结点中(父结点一定是索引类型结点),父结点
左侧孩子指针向左结点,右侧孩子指针向右结点。将当前结点的指针指向父结点,然后执行第3步;
- 若当前非叶子结点
的个数不多于
,则插入结束。否则,将这个索引类型结点分裂成两个索引结点,左索引结点包含前
个key,右结点包含
个
,将第
个
进位到父结点中,父结点的
左孩子指向左结点,父结点的
右孩子指向右结点。将当前结点的指针指向父结点,然后重复第3步。
下面是一颗阶
树的插入过程,
阶
数的非根结点最少
个
,最多4个
。
a)空树中插入
b)依次插入,
,
c)插入
插入后超过了关键字的个数限制,所以要进行分裂。在叶子结点分裂时,分裂出来的左结点
个记录,右边
个记录,中间
成为索引结点中的
,分裂后当前结点指向父结点(根结点)。结果如下图所示。
当然还有另一种分裂方式,给左结点个记录,右结点
个记录,此时索引结点中的
就变为
。
d)插入
e)插入,插入后如下图所示
当前结点的关键字个数大于,进行分裂。分裂成两个结点,左结点
个记录,右结点
个记录,
进到父结点(索引类型)中,将当前结点的指针指向父结点。
当前结点的关键字个数满足条件,插入结束。
f)插入若干数据后
g)在上图中插入,结果如下图所示
当前结点的关键字个数超过,需要分裂。左结点
个记录,右结点
个记录。分裂后关键字
进入到父结点中,将当前结点的指针指向父结点,结果如下图所示。
当前结点关键字个数超过,需要继续分裂。左结点
个关键字,右结点
个关键字,
进入父结点中,将当前结点指向父结点,结果如下图所示。
当前结点的关键字个数满足条件,插入结束。
2.3 B+树的删除操作
如果叶子结点中没有相应的,则删除失败。否则执行下面的步骤
- 删除叶子结点中对应的
。删除后若结点的
的个数大于等于
,删除操作结束,否则执行第
步;
- 若兄弟结点
有大于
,向兄弟结点借一个
,同时用借到的
替换父结点(指当前结点和兄弟结点共同的父结点)中的
,删除结束。否则执行第
步;
- 若兄弟结点中没有富余的
,则当前结点和兄弟结点合并成一个新的叶子结点,并删除父结点中的
(父结点中的这个
两边的孩子指针就变成了一个指针,正好指向这个新叶子结点),将当前结点指向父结点(必为索引结点),执行第
步(以后操作和
树完全一样,主要是为了更新索引结点);
- 若索引结点的
的个数大于等于
,则删除操作结束,否则执行第
步;
- 若兄弟结点有富余,父结点
下移,兄弟结点
上移,删除结束,否则执行第
步;
- 当前结点和兄弟结点及父结点下移
合并成一个新的结点。将当前结点指向父结点,重复第
步。
注意,通过树的删除操作后,索引结点中存在的
,不一定在叶子结点中存在对应的记录。
下面是一颗阶
树的删除过程,
阶
数的结点最少
个
,最多
个
。
a)初始状态
b)删除,删除后结果如下图
删除后叶子结点中的个数大于等于
,删除结束
c)删除,删除后的结果如下图所示
删除后当前结点只有个
,不满足条件,而兄弟结点有
个
,可以从兄弟结点借
个
为
的记录,同时更新将父结点中的关键字由
也变为
,删除结束。
d)删除,删除后的结果如下图所示
当前结点关键字个数小于,(左)兄弟结点中的也没有富余的关键字(当前结点还有个右兄弟,不过选择任意一个进行分析就可以了,这里我们选择了左边的),所以当前结点和兄弟结点合并,并删除父结点中的
,当前结点指向父结点。
此时当前结点的关键字个数小于,兄弟结点的关键字也没有富余,所以父结点中的关键字下移,和两个孩子结点合并,结果如下图所示。
相关文章:

B树和B+树的插入、删除
1. B树 1.1 B树的定义 树也称树,它是一颗多路平衡查找树。我们描述一颗树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,用字母表示阶数。当取时,就是我们常见的二叉搜索树。 一颗阶的树定义如下: 每…...

Axios网络请求总结
在实际项目开发中,前端页面所需要的数据往往需要从服务器端获取,这必然涉及与服务器的通信。Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。Axios 在浏览器端使用XMLHttpRequests发送网络请求,并能自动完成JSON…...

立仪科技光谱共焦应用之金属隔膜静态重复性测量
01|检测需求:金属隔膜重复性测量 02|检测方式 为了保证精度,首先先用千分尺进行测量,得出相应的厚度数据,在选择合适的侧头,根据结果,我们现在立仪科技H4UO控制器搭配D27A20侧头 03&…...

vue3实现video视频+弹幕评论
vue3实现视频加评论 之前写了一篇博客使用了弹幕插件http://t.csdnimg.cn/616mlvue3 使用弹幕插件,今天对这个页面进行了升级 变成了 vue3使用video 这个没有使用插件,昨天看了好多,没发现有用的插件,下载了几个都没办法使用就用…...

STM32-OTA升级
一、OTA(Over-The-Air) OTA(Over-The-Air)是一种通过无线通信方式,为设备分发新软件、配置甚至更新加密密钥的技术。它允许中心位置向所有用户发送更新,确保每个接收者都无法拒绝、破坏或改变这些更新&…...

一种JSON多态表示法
介绍 假设现在需要实现一种功能: 从某个远程的组件(消息队列或远程文件)拉取最后几条记录做一个展示. 需要支持如下的组件: Kafka RocketMQ OSS 假设还有很多, 这里不列了 … 显然, 每种组件需要的参数各不一样, 那么此时如何使用一个统一的结构来表达这些组件的参数呢?…...

C语言实现单链表
一、什么是单链表 1.链表就是一种在物理存储上各个节点非连续的,随机的,元素的逻辑顺序是通过链表中的指针链接的次序而实现的。 图示: 二、单链表中节点的定义 #include<stdio.h> #include<stdlib.h> #include<string.h>…...

循环神经网络三
一.介绍 在普通的神经网络中,信息的传递是单向的,这种限制虽然使得网络变得更容易学习,单在一定程度上也减弱了神经网络模型的能力。特别是在现实生活中,网络的输出不仅和当前时刻的输入相关,也过去一段时间的输出相关…...

优购电商小程序的设计
管理员账户功能包括:系统首页,个人中心,用户管理,商品分类管理,商品信息管理,留言板管理,订单管理,系统管理 微信端账号功能包括:系统首页,商品信息…...

【ARM】v8架构programmer guide(4)_ARMv8的寄存器
目录 4.4Endianness(端序或字节序) 4.5 改变execution state 4.5.1 Registers at AArch32 4.5.2 PSTATE at AArch32 4.6 NEON 和浮点数寄存器 4.6.1 AArch64中浮点寄存器的组织结构 4.6.2 标量寄存器大小 4.6.3 向量寄存器大小 4.6.4 NEON在AArc…...

Java设计模式详细讲解
目录 设计模式概述 1.1 什么是设计模式1.2 设计模式的类型1.3 设计模式的历史与发展1.4 设计模式在软件开发中的重要性 创建型模式 2.1 单例模式2.2 工厂方法模式2.3 抽象工厂模式2.4 建造者模式2.5 原型模式 结构型模式 3.1 适配器模式3.2 装饰器模式3.3 代理模式3.4 外观模…...

图论------弗洛伊德(Floyd-Warshall)算法
题目描述: 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的 T-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助…...

C#实现动画效果
在C#中,实现动画效果通常可以使用Windows Forms的Timer类或者使用System.Windows.Media.Animation命名空间下的类(如果是WPF应用)。以下是一个Windows Forms应用中使用Timer类来创建简单的动画效果的例子。 假设我们有一个窗体(F…...

Git 对比 SVN 的区别和优势
引言 版本控制系统(VCS)是软件开发过程中不可或缺的一部分,它们用于管理代码的变更、协调开发团队的工作。Git 和 SVN(Apache Subversion)是目前最流行的两个版本控制系统。本文将详细分析 Git 和 SVN 的区别及各自的…...

Qt实现无边框窗口的拖动和缩放
在使用QT创建窗体的时候,为了使窗口美化,通常不使用QT自带的边框。会调用下面函数去除窗体边框。 setWindowFlags(Qt::FramelessWindowHint) 但是有个问题,当去除了QT自带边框后,窗体就变得不能移动了,也不能改变窗口大…...

入门岛2-python实现wordcount并进行云端debug
书生大模型学习 任务: 1.实现一个wordcount函数,统计英文字符串中每个单词出现的次数。返回一个字典,key为单词,value为对应单词出现的次数。 2.Vscode连接InternStudio debug TIPS:记得先去掉标点符号,然后把每个单词…...

c语言-链表1
10 链表 一、链表是什么? -- 数据的一种存储方式 -- 链式存储 (1)线性存储 -- 地址连续 -- 自动开辟,自动释放 -- 默认是线性存储 (2)链式存储 -- 地址不连续…...

你好! Git——企业级开发模型
企业级开发模型(6) 一、删除远程分支,git branch -a (查看所有本地分支与远程分支)还能看到已经删除的分支,怎么解决?二、企业级开发流程2.1 企业级开发流程2.2 系统开发环境 三、Git分支设计模…...

力扣面试150 查找和最小的 K 对数字 最小堆 去重
Problem: 373. 查找和最小的 K 对数字 👨🏫 参考题解 class Solution {public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {// 创建一个大小为 k 的结果列表,用于存储和最小的 k 个数对List<Li…...

Oceanbase 执行计划
test100 CREATE TABLE `test100` ( `GRNT_CTR_NO` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT 担保合同编号, `GRNT_CTR_TYP` varchar(3) COLLATE utf8mb4_bin NOT NULL COMMENT 担保合同类型, `COLC_GRNT_IND` varchar(1) COLLATE utf8mb4_bin DEFAULT NULL …...

精品丨模型关系介绍
PowerBI中的模型关系相信小伙伴们都不会感觉到陌生,因为一份优秀的报表无法离开数据模型的支撑。 对比其它BI类工具而言,白茶认为其建模功能才是最为突出的功能点。 模型关系类型 PowerBI中我们常用的模型关系一共包含5类: 一对一关系(1:1) …...

CentOS7 配置 nginx 和 php 方案
配置方案 一、安装软件二、编写配置文件,连接PHP三、引用文件四、测试 鉴于网上教程错综复杂,写下一这篇文章 本教程只需要三步即可 一、安装软件 yum install -y nginx php php-fpm二、编写配置文件,连接PHP 一般情况下在安装完 nginx 后…...

Promise.all全面解析:使用方法与实战技巧
Promise是JavaScript中处理异步操作的重要机制,它提供了一种优雅的方式来处理异步回调,避免了传统回调地狱的问题。而Promise.all作为Promise的一个静态方法,更是在处理多个异步操作时发挥着关键作用。本文将全面解析Promise.all的使用方法&a…...
NLP从零开始------9文本进阶处理之文本相似度计算
1.文本相似度计算简介 在自然语言处理中,经常会涉及度量两个文本相似度的问题。在诸如对话系统和信息减速等中,度量句子或短语之间的相似度尤为重要。在新闻学传媒中应用文本相似度可以帮助读者快速检索到想要了解的报道。 文本相似度的定义式如下所示&a…...

Electron 在 MAC 上的 build 签名应用配置
Electron 在 MAC 上的 build 签名应用配置涉及多个步骤,包括准备开发者账号、生成证书和配置文件、配置环境变量以及使用适当的工具进行签名和公证。以下是一个详细的配置流程: 一、准备开发者账号 首先,你需要在 Apple 开发者网站 注册并拥有一个开发者账号。这个账号将用…...

15 交换机命令行配置
交换机命令行配置 一、交换机命令行基本配置 (一)配置主机名 Switch>enable Switch#configure terminal Switch(config)#hostname S1(二)查看配置信息 Switch#show running-config Building configuration...Current confi…...

工作流之Flowable与SpringBoot结合
文章目录 1 Flowable1.1 flowable-ui部署运行1.2 绘制流程图1.2.1 绘制1.2.2 绘图细节1.2.3 bpmn文件导入 1.3 后台项目搭建1.3.1 pom.xml1.3.2 数据库表说明 1.4 流程引擎API与服务1.4.1 主要API1.4.2 示例 1 Flowable 1.1 flowable-ui部署运行 flowable-6.6.0 运行 官方dem…...

python实战:数据分析基础知识
当涉及到数据分析和统计建模时,Python 提供了强大的工具和库,如 pandas、numpy、statsmodels 和 matplotlib。本文将以一个实际的案例为例,介绍如何利用这些工具进行回归分析,并通过可视化工具进行结果展示和解释。 1. 背景介绍 …...

Grafana深入讲解
Grafana 深入讲解 目录 概述Grafana 基本概念 2.1 Grafana 简介2.2 Grafana 功能特性2.3 Grafana 架构 Grafana 安装与配置 3.1 安装 Grafana3.2 配置 Grafana3.3 验证 Grafana 安装 Grafana 数据源 4.1 支持的数据源类型4.2 添加数据源4.3 配置 Prometheus 数据源 Grafana 仪…...

002 git
下载 使用git clone命令下载特定分支 打开终端或命令行界面。 使用cd命令切换到你想存放仓库副本的本地目录。 使用以下命令克隆仓库的develop分支到本地(注意替换<仓库URL>为实际的仓库URL): git clone -b develop --single-branch…...