c++实现B树(下)
书接上回小吉讲的是B树的搭建和新增方法的实现(blog传送门🚪:B树实现上)(如果有小可爱对B树还不是很了解的话,可以先看完上一篇blog,再来看小吉的这篇blog)。那这一篇主要讲的是B树中删除操作的实现。
在看小吉的数据结构与算法(c++实现)系列博客中,各位小可爱们应该能发现,其实在实现一个数据结构时,删除操作普遍比新增操作要难。小吉浅浅预告一下B树的删除操作可以说是天花板级别的难。但是各位小可爱们不要怕,跟着小吉的博客看下去B树删除易如反掌(哈哈,夸张了🤣)
在实现B树删除操作之前,小吉先在这声明一下:B树的删除是删除某个节点的key并不是将节点删除(无需使用delete)
九个成员方法
在正式分析B树删除之前,先要实现9个方法(不要害怕,这九个方法都非常简单),这九个方法在后期实现删除操作代码时大有用处(可以小小期待一下)
int removeKey(int index);//移除指定index处的key
int removeRightmostKey();//移除最右边的key
Node* removeChild(int index);//移除指定index处的child
Node* removeLeftmostChild();//移除最左边的child
Node* removeRightmostChild();//移除最右边的child
Node* childLeftSibling(int index);//index孩子处左边的兄弟
Node* childRightSibling(int index);//index孩子处右边的兄弟
void moveToTarget(Node* target);//复制当前节点的所有key和child到target
这些方法都定义在Node节点类中,这些方法都比较简单,小吉在这就不详细讲解了,直接上代码。
int Node::removeKey(int index)
{int t = _keys[index];_keys.erase(_keys.begin() + index);KeyNumber--;return t;
}int Node::removeLeftmostKey()
{return removeKey(0);
}int Node::removeRightmostKey()
{return removeKey(KeyNumber - 1);
}Node* Node::removeChild(int index)
{Node* t = _children[index];_children.erase(_children.begin() + index);return t;
}Node* Node::removeLeftmostChild()
{return removeChild(0);
}Node* Node::removeRightmostChild()
{return removeChild(KeyNumber - 1);
}Node* Node::childLeftSibling(int index)
{return index > 0 ? _children[index - 1] : nullptr;
}Node* Node::childRightSibling(int index)
{return index == KeyNumber ? nullptr : _children[index + 1];
}void Node::moveToTarget(Node* target)
{int start = target->KeyNumber;if (!this->_leaf){for (int i = 0; i <= this->KeyNumber; i++){target->_children[start + i] = this->_children[i];}}for (int i = 0; i < this->KeyNumber; i++){target->_keys[target->KeyNumber++] = this->_keys[i];}
}
搭建remove删除方法的架子
首先明确一点要删除节点的前提是要先找到节点才能进行删除操作,所以remove方法最初的雏形就是找要删除的节点(采用递归进行查找)
找节点的思路在上一篇blog新增节点时有体现,小吉在这也不详细讲解了,直接上代码👇
void BTree::remove(int key)
{doremove(_root, key,0);
}void BTree::doremove(Node* node, int key)
{int i = 0;while (i < node->KeyNumber){if (node->_keys[i] >= key){break;}i++;}if (node->_leaf)//叶子节点{if (i < node->KeyNumber && node->_keys[i] == key)//i找到:代表待删除key的索引{}else//i没找到{return;}}else//非叶子节点{if (i < node->KeyNumber && node->_keys[i] == key)//i找到:代表待删除key的索引{}else//i没找到:代表第i个孩子继续查找{doremove(node,node->_children[i], key,i);}}
}
remove删除情况分类
主要分为以下4种情况:
case1:当前节点是叶子节点,没找到
case2:当前节点是叶子节点,找到了
case3:当前节点是非叶子节点,没找到
case4:当前节点是非叶子节点,找到了
叶子节点
分析当前节点是叶子节点的情况,这种情况比较简单,找到就删除当前节点要删除的key值(调用前面实现的九个小方法中的removeKey方法即可),没找到说明B树中没有我们要删除的key值,直接return退出即可。
if (node->_leaf)//叶子节点{if (i < node->KeyNumber && node->_keys[i] == key)//i找到:代表待删除key的索引{node->removeKey(i);}else//i没找到{return;}}
非叶子节点
没找到:递归继续寻找
找到:1.找后继的key 2.替换待删除的key 3.删除后继的key
else//非叶子节点{if (i < node->KeyNumber && node->_keys[i] == key)//i找到:代表待删除key的索引{//1.找到后继keyNode* s = node->_children[i + 1];while (!s->_leaf){s = s->_children[0];}int skey = s->_keys[0];//2.替换待删除keynode->_keys[i] = skey;//3.删除后继keydoremove(node,node->_children[i + 1], skey,i+1);}else//i没找到:代表第i个孩子继续查找{doremove(node,node->_children[i], key,i);}}
删除完不平衡的处理
在删除节点完可能存在删除后key数目<下限(根节点除外),导致不平衡的情况
对平衡的调整又可以分为一下三种情况:
case1:左边富裕,右旋
case2:右边富裕,左旋
case3:两边都不够借,向左合并
void balance (Node* parent,Node* x,int i) //x为要调整的节点,i为被调整孩子的索引
注: 为了实现平衡函数的传参,doremove方法还要再加上两个参数void BTree::doremove(Node* parent,Node* node, int key,int index)//index被删除节点的索引
左边富裕,右旋

右旋:1)父节点中前驱key旋转下来(前驱key是要删除节点的前驱)
2)left中最大的孩子换爹(被调整节点的左兄弟不为叶子节点要执行这一步)
2)left中最大的的key旋转上去
下面👇是代码实现旋转的过程:
void BTree::balance(Node* parent, Node* x, int i)
{Node* left = parent->childLeftSibling(i);if (left != nullptr && left->KeyNumber > MIN_KEYNUMS){//左边富裕,右旋x->insertKey(0, parent->_keys[i - 1]);if (!left->_leaf){x->insertChild(0, left->removeRightmostChild());}parent->_keys[i-1]= left->removeRightmostKey();return;}
}
右边富裕,左旋
左旋:1)父结点中后继key旋转下来(后继key是要删除节点的后继)
2)right中最小的孩子换爹(被调整节点的右兄弟不为叶子节点时要执行这一步)
3)right中最小的key旋转上去
和左边富裕,右旋的情况类似,小吉这里就不画图了,直接上代码
void BTree::balance(Node* parent, Node* x, int i)
{Node* right = parent->childRightSibling(i);if (right != nullptr && right->KeyNumber > MIN_KEYNUMS){//右边富裕,左旋x->insertKey(x->KeyNumber, parent->_keys[i]);if (!right->_leaf){x->insertChild(x->KeyNumber+1, right->removeLeftmostChild());}parent->_keys[i] = right->removeLeftmostKey();return;}
两边都不够借,向左合并
case1:被调整节点有左兄弟,往左兄弟处合并
case2:被调整节点没有左兄弟,父节点和右边的兄弟向自己处合并
case1:

case2:

代码呈现👇:
//两边都不够借,向左合并if (left != nullptr){//向左兄弟合并parent->removeChild(i);left->insertKey(left->KeyNumber, parent->removeKey(i - 1));x->moveToTarget(left);}else{//向自己合并parent->removeChild(i + 1);x->insertKey(x->KeyNumber, parent->removeKey(i));right->moveToTarget(x);}
到这里,B树删除的所有涉及到的知识点都已经讲完了,下面小吉给大家提供一个B树删除的测试代码。
B树删除的测试代码
void testRemove()
{BTree tree(3);for (int i = 1; i <= 6; i++){tree.put(i);}tree.remove(2);Node* root = tree._root;Node* leftchild = root->_children[0];cout << root->_keys[0] << endl;for (int i = 0; i < leftchild->KeyNumber; i++){cout << leftchild->_keys[i] << ' ';}cout << endl;
}
到这里,这篇blog要结束了,有点小长,也有点小难,可能一次性看完对一些小可爱们有难度,不要急,慢慢看,多给自己一点时间❤️
(其实这篇博客从9月份写到了11月🤣,可以说是小吉已经发表的博客中最长的一篇了,也是最难的一篇,原计划打算九月份写完的,中途去备考了一下软设,所以就一直拖到现在才写完,不好意思耽误了这么久了😖)
&emps;最后,创作不易(这篇blog小吉真的写了很久),还望大家多多支持(点赞收藏关注小吉🌹)
相关文章:
c++实现B树(下)
书接上回小吉讲的是B树的搭建和新增方法的实现(blog传送门🚪:B树实现上)(如果有小可爱对B树还不是很了解的话,可以先看完上一篇blog,再来看小吉的这篇blog)。那这一篇主要讲的是B树中…...
外星人入侵
学习于Python编程从入门到实践(Eric Matthes 著) 整体目录:外星人入侵文件夹是打包后的不必在意 图片和音效都是网上下载的 音效下载网站:Free 游戏爆击中 Sound Effects Download - Pixabay 运行效果:可以上下左右移…...
【数据仓库】hbase的安装与简单操作
HBase 是一个分布式的、面向列的开源数据库,它支持大规模数据存储,并且是 Hadoop 生态系统的一部分。HBase 能够在廉价的硬件上运行,并提供对大量数据的随机、实时读写访问。下面是关于如何在 Linux 系统上安装 HBase 以及进行一些基本操作的…...
为什么RNN(循环神经网络)存在梯度消失和梯度爆炸?
1️⃣ 原理分析 RNN前向传播的公式为: x t x_t xt是t时刻的输入 s t s_t st是t时刻的记忆, s t f ( U ⋅ x t W ⋅ s t − 1 ) s_tf(U\cdot x_tW\cdot s_{t-1}) stf(U⋅xtW⋅st−1),f表示激活函数, s t − 1 s_{t-1} …...
【数据库】数据库迁移的注意事项有哪些?
数据库迁移是一个复杂且关键的过程,需要谨慎处理以确保数据的完整性和应用程序的正常运行。以下是一些数据库迁移时需要注意的事项: 1. 充分的前期准备 1.1 评估迁移需求 明确目标:确定迁移的具体目标,例如添加新字段、修改现…...
MQTT协议解析 : 物联网领域的最佳选择
1. MQTT协议概述 1.1 MQTT协议是什么 MQTT : Message Queuing Telemetry Transport 模式 : 发布 / 订阅主题优点 : 代码量小、低带宽、实时可靠应用 : 物联网、小型设备、移动应用MQTT 常用端口 : 1883 MQTT是一个网络协议,和HTTP类似,因为轻量简单&…...
pycharm中from[本地包]import文件/模块出现问题(最最最全方法!)
1.通过PYTHONPATH的方法在此处将路径添加上,能够让IDE访问得到。 2.通过选中目标文件所在的文件的文件夹单击右键,如下图所示可以看到下方的mark directory as选项中存在 存在excluded,选择此项可解决问题,如果仍有问题可以尝试其…...
MongoDB在现代Web开发中的应用
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 引言 MongoDB 概述 定义与原理 发展…...
Python Bokeh 数据可视化教程
Python Bokeh 数据可视化教程 引言 在数据科学和分析的过程中,数据可视化是一个至关重要的环节。它不仅能帮助我们更好地理解数据,还能在报告和展示中提升数据的可读性和吸引力。Python 作为数据科学的主要工具之一,提供了多种数据可视化库…...
(一)<江科大STM32>——软件环境搭建+新建工程步骤
一、软件环境搭建 (1)安装 Keil5 MDK 文件路径:江科大stm32入门教程资料/Keil5 MDK/MDK524a.EXE,安装即可,路径不能有中文。 (2)安装器件支持包 文件路径:江科大stm32入门教程资料…...
内存大小的单位转换
计算机中内存大小的单位转换通常是按照以下规则进行的: 基本单位 1 字节 (Byte) 8 位 (bit) 常见的内存单位及转换关系 1 字节 (Byte) 8 位 (bit)1 千字节 (KB) 1,024 字节 (B)1 兆字节 (MB) 1,024 千字节 (KB) 1,024 * 1,024 字节 (B)1 吉字节 (GB) 1,02…...
如何在 Spring MVC 中使用 `@PostMapping`? 如何在 Spring MVC 中使用 `@PutMapping`?
PostMapping 和 PutMapping 是 Spring MVC 中用于处理 HTTP POST 和 PUT 请求的注解。它们分别对应 HTTP 协议中的 POST 和 PUT 方法,通常用于创建和更新资源。下面详细解释如何在 Spring MVC 中使用这两个注解。 1. 使用 PostMapping PostMapping 注解用于处理 H…...
AIGC Agent(智能体)应用开发高级工程师实战培训 —— 线上8周系统教学课程学习路线图
🎯 课程目标 系统掌握AIGC核心技术:学员将通过项目驱动学习,从文本生成、图像创意到智能体开发,全面进阶AIGC技术,探索其在营销、教育、数据处理、知识管理等领域的实际应用。构建AIGC智能体服务体系:学成…...
GDSC、CTRP数据库学习
GDSC 写在前面下载数据疑问1.GDSC、CTRP数据里有TCGA配套的数据?数据类型?CTRP原始数据如何处理 写在前面 开此贴做GDSC的数据分析记录 下载数据 GDSC官网:http://www.cancerrxgene.org/ 由于在官网下载数据过于麻烦,于是我使用…...
【嵌入式】ESP32开发(一)ESP-IDF概述
文章目录 1 前言2 IDF环境配置3 在VS Code中使用IDF3.1 使用ESP-IDF例程3.2 底部按钮的作用【重要!】3.3 高级用法4 ESP-IDF框架分析5 从零开始创建一个项目5.1 组件(component)6 主要参考资料7 遇到的一些问题与解决办法8 对于ESP-IDF开发的一些感受1 前言 对于ESP32的开发…...
最新6.7分非肿瘤纯生信,使用机器学习筛选慢阻肺中的关键基因。机器学习在非肿瘤生信文章中正火,可重复!
关于非肿瘤生信,我们也解读过很多,主要有以下类型 1 单个疾病WGCNAPPI分析筛选hub基因。 2 单个疾病结合免疫浸润,铁死亡,自噬等基因集,机器学习算法等。 3 两种相关疾病联合分析,包括非肿瘤结合非肿瘤&…...
vue 提交表单抹除字段为空的数据
使用背景 在配合后端post请求接口的时候 仅需要将有值的字段传入接口中 关键代码 cleanDataObj(obj) {Object.keys(obj).forEach((key) > {if (obj[key] ) {delete obj[key]}})},demo如下 export default {data() {return {demoObject:{name:小花,sex:,hobb…...
web实验3:虚拟主机基于不同端口、目录、IP、域名访问不同页面
创建配置文件: 创建那几个目录及文件,并且写内容: 为网卡ens160添加一个 IPv4 地址192.168.234.199/24: 再重新激活一下网卡ens160: 重启服务: 关闭防火墙、改宽松模式: 查看nginx端口监听情况:…...
英伟达Isaac Manipulator产品体验
相关配置 Isaac Manipulator3.1.0Isaac Sim4.2.0Ubuntu20.04GPURTX 4090 LaptopCPUI9 13900HXMem64GB 过程记录与反馈 GPU加速效果 请描述您在使用Isaac Manipulator时,调用cuMotion加速库来进行机器人运动规划和轨迹优化等任务的步骤和过程,并记录任…...
网安加·百家讲坛 | 仝辉:金融机构鸿蒙应用安全合规建设方案
作者简介:仝辉,北京娜迦信息科技发展有限公司攻防安全负责人,深耕移动应用安全领域十余年,获得过CISP、CISSP、OSCP、PMP、CCRC-CIASW等相关证书,参与多项移动应用安全标准起草,参与华为、平安集团、中国移…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

