当前位置: 首页 > 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;参与华为、平安集团、中国移…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...

Python常用模块:time、os、shutil与flask初探

一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学

一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件&#xff0c;其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时&#xff0c;价带电子受激发跃迁至导带&#xff0c;形成电子-空穴对&#xff0c;导致材料电导率显著提升。…...

职坐标物联网全栈开发全流程解析

物联网全栈开发涵盖从物理设备到上层应用的完整技术链路&#xff0c;其核心流程可归纳为四大模块&#xff1a;感知层数据采集、网络层协议交互、平台层资源管理及应用层功能实现。每个模块的技术选型与实现方式直接影响系统性能与扩展性&#xff0c;例如传感器选型需平衡精度与…...

论文笔记:Large Language Models for Next Point-of-Interest Recommendation

SIGIR 2024 1 intro 传统的基于数值的POI推荐方法在处理上下文信息时存在两个主要限制 需要将异构的LBSN数据转换为数字&#xff0c;这可能导致上下文信息的固有含义丢失仅依赖于统计和人为设计来理解上下文信息&#xff0c;缺乏对上下文信息提供的语义概念的理解 ——>使用…...