当前位置: 首页 > news >正文

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树的搭建和新增方法的实现&#xff08;blog传送门&#x1f6aa;&#xff1a;B树实现上&#xff09;&#xff08;如果有小可爱对B树还不是很了解的话&#xff0c;可以先看完上一篇blog&#xff0c;再来看小吉的这篇blog&#xff09;。那这一篇主要讲的是B树中…...

外星人入侵

学习于Python编程从入门到实践&#xff08;Eric Matthes 著&#xff09; 整体目录&#xff1a;外星人入侵文件夹是打包后的不必在意 图片和音效都是网上下载的 音效下载网站&#xff1a;Free 游戏爆击中 Sound Effects Download - Pixabay 运行效果&#xff1a;可以上下左右移…...

【数据仓库】hbase的安装与简单操作

HBase 是一个分布式的、面向列的开源数据库&#xff0c;它支持大规模数据存储&#xff0c;并且是 Hadoop 生态系统的一部分。HBase 能够在廉价的硬件上运行&#xff0c;并提供对大量数据的随机、实时读写访问。下面是关于如何在 Linux 系统上安装 HBase 以及进行一些基本操作的…...

为什么RNN(循环神经网络)存在梯度消失和梯度爆炸?

1️⃣ 原理分析 RNN前向传播的公式为&#xff1a; x t x_t xt​是t时刻的输入 s t s_t st​是t时刻的记忆&#xff0c; s t f ( U ⋅ x t W ⋅ s t − 1 ) s_tf(U\cdot x_tW\cdot s_{t-1}) st​f(U⋅xt​W⋅st−1​)&#xff0c;f表示激活函数&#xff0c; s t − 1 s_{t-1} …...

【数据库】数据库迁移的注意事项有哪些?

数据库迁移是一个复杂且关键的过程&#xff0c;需要谨慎处理以确保数据的完整性和应用程序的正常运行。以下是一些数据库迁移时需要注意的事项&#xff1a; 1. 充分的前期准备 1.1 评估迁移需求 明确目标&#xff1a;确定迁移的具体目标&#xff0c;例如添加新字段、修改现…...

MQTT协议解析 : 物联网领域的最佳选择

1. MQTT协议概述 1.1 MQTT协议是什么 MQTT : Message Queuing Telemetry Transport 模式 : 发布 / 订阅主题优点 : 代码量小、低带宽、实时可靠应用 : 物联网、小型设备、移动应用MQTT 常用端口 : 1883 MQTT是一个网络协议&#xff0c;和HTTP类似&#xff0c;因为轻量简单&…...

pycharm中from[本地包]import文件/模块出现问题(最最最全方法!)

1.通过PYTHONPATH的方法在此处将路径添加上&#xff0c;能够让IDE访问得到。 2.通过选中目标文件所在的文件的文件夹单击右键&#xff0c;如下图所示可以看到下方的mark directory as选项中存在 存在excluded&#xff0c;选择此项可解决问题&#xff0c;如果仍有问题可以尝试其…...

MongoDB在现代Web开发中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 引言 MongoDB 概述 定义与原理 发展…...

Python Bokeh 数据可视化教程

Python Bokeh 数据可视化教程 引言 在数据科学和分析的过程中&#xff0c;数据可视化是一个至关重要的环节。它不仅能帮助我们更好地理解数据&#xff0c;还能在报告和展示中提升数据的可读性和吸引力。Python 作为数据科学的主要工具之一&#xff0c;提供了多种数据可视化库…...

(一)<江科大STM32>——软件环境搭建+新建工程步骤

一、软件环境搭建 &#xff08;1&#xff09;安装 Keil5 MDK 文件路径&#xff1a;江科大stm32入门教程资料/Keil5 MDK/MDK524a.EXE&#xff0c;安装即可&#xff0c;路径不能有中文。 &#xff08;2&#xff09;安装器件支持包 文件路径&#xff1a;江科大stm32入门教程资料…...

内存大小的单位转换

计算机中内存大小的单位转换通常是按照以下规则进行的&#xff1a; 基本单位 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 方法&#xff0c;通常用于创建和更新资源。下面详细解释如何在 Spring MVC 中使用这两个注解。 1. 使用 PostMapping PostMapping 注解用于处理 H…...

AIGC Agent(智能体)应用开发高级工程师实战培训 —— 线上8周系统教学课程学习路线图

&#x1f3af; 课程目标 系统掌握AIGC核心技术&#xff1a;学员将通过项目驱动学习&#xff0c;从文本生成、图像创意到智能体开发&#xff0c;全面进阶AIGC技术&#xff0c;探索其在营销、教育、数据处理、知识管理等领域的实际应用。构建AIGC智能体服务体系&#xff1a;学成…...

GDSC、CTRP数据库学习

GDSC 写在前面下载数据疑问1.GDSC、CTRP数据里有TCGA配套的数据&#xff1f;数据类型&#xff1f;CTRP原始数据如何处理 写在前面 开此贴做GDSC的数据分析记录 下载数据 GDSC官网&#xff1a;http://www.cancerrxgene.org/ 由于在官网下载数据过于麻烦&#xff0c;于是我使用…...

【嵌入式】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分非肿瘤纯生信,使用机器学习筛选慢阻肺中的关键基因。机器学习在非肿瘤生信文章中正火,可重复!

关于非肿瘤生信&#xff0c;我们也解读过很多&#xff0c;主要有以下类型 1 单个疾病WGCNAPPI分析筛选hub基因。 2 单个疾病结合免疫浸润&#xff0c;铁死亡&#xff0c;自噬等基因集&#xff0c;机器学习算法等。 3 两种相关疾病联合分析&#xff0c;包括非肿瘤结合非肿瘤&…...

vue 提交表单抹除字段为空的数据

使用背景 在配合后端post请求接口的时候 仅需要将有值的字段传入接口中 关键代码 cleanDataObj(obj) {Object.keys(obj).forEach((key) > {if (obj[key] ) {delete obj[key]}})},demo如下 export default {data() {return {demoObject:{name:小花&#xff0c;sex:,hobb…...

web实验3:虚拟主机基于不同端口、目录、IP、域名访问不同页面

创建配置文件&#xff1a; 创建那几个目录及文件&#xff0c;并且写内容&#xff1a; 为网卡ens160添加一个 IPv4 地址192.168.234.199/24: 再重新激活一下网卡ens160&#xff1a; 重启服务&#xff1a; 关闭防火墙、改宽松模式&#xff1a; 查看nginx端口监听情况&#xff1a;…...

英伟达Isaac Manipulator产品体验

相关配置 Isaac Manipulator3.1.0Isaac Sim4.2.0Ubuntu20.04GPURTX 4090 LaptopCPUI9 13900HXMem64GB 过程记录与反馈 GPU加速效果 请描述您在使用Isaac Manipulator时&#xff0c;调用cuMotion加速库来进行机器人运动规划和轨迹优化等任务的步骤和过程&#xff0c;并记录任…...

网安加·百家讲坛 | 仝辉:金融机构鸿蒙应用安全合规建设方案

作者简介&#xff1a;仝辉&#xff0c;北京娜迦信息科技发展有限公司攻防安全负责人&#xff0c;深耕移动应用安全领域十余年&#xff0c;获得过CISP、CISSP、OSCP、PMP、CCRC-CIASW等相关证书&#xff0c;参与多项移动应用安全标准起草&#xff0c;参与华为、平安集团、中国移…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...