c++实现B树(上)
哈喽啊!好久不见,甚是想念!失踪人口要回归了,时隔一个多月小吉我终于要更新blog了🎉。在停更的一个多月中,小吉也有在好好学习提升自己,立志给大家呈现好文章。
现在让我们进入正题吧,今天我们这篇blog主要讲用c++实现B树,上一篇blog讲的是二叉搜索树,中间还有avl树和红黑树小吉还没有把博客写出来。希望大家多多关注小吉,在完成这篇blog后,小吉将会出avl树实现的博客(浅浅期待一下吧)。
老规矩,在正式实现B树之前,我们先来了解了解什么是B树。
B树的概念
B树:B树是一种自平衡的树形数据结构,用于解决磁盘存储器上的数据管理问题,减少磁盘i/o操作的次数,从而提高数据访问的效率。
B树的特征
在讲B树的特征之前,我们先来了解一下度和阶的概念
度(degree):指树中节点孩子数
阶(order):指所有节点孩子数最大值
在m阶的B树中,即孩子数最大值为m
B树的特征
1.B树的每个节点可以有多个子结点,每个节点最多可以有m个子节点;除根节点和叶子节点外,其他每个节点至少有ceil(m/2)个孩子。(ceil是指向上取整)
2.每个节点的关键字(键值)数量与子节点数量相关,通常比子节点数量少1。每个节点中的关键字数量k满足ceil(m/2)-1<=k<=m-1。
3.B树的所有叶子节点都在同一层
4.B树通过动态调整节点中的关键字数量来保持树的平衡
5.B树中的关键字按照升序排序
6.父节点中第i个关键字小于其第i个子节点的所有关键字,且大于其第i-1个子节点中所有的关键字

(小吉在这里提醒一下:一定要好好理解B树的特征,后面在实现的过程有什么不能理解的地方,一定要回头好好读一下特征,大部分问题都能在看过特征后得到解答(小吉的小经验✨))
B树节点类
_t代表最小度数,也就是最小的孩子数ceil(m/2),因此最大孩子数m可以由2*t表示
节点类代码呈现👇:
class Node
{
public:Node(int t, bool leaf = true) :_t(t), _leaf(left), _children(2 * t, nullptr),_keys(2*t-1),KeyNumber(0){}//初始化列表
public:vector<int> _keys;//键值vector<Node*> _children;//孩子int KeyNumber;//有效键值数目bool _leaf;//是否为叶子节点int _t;//最小度数(最小孩子数)
};
B树节点类成员方法
主要实现3个方法
1.Node* get(int key);//多路查找
2.void insertKey(int index,int key);//向指定索引处插入key
3.void insertChild(int index, Node* child);//向指定索引处插入child
代码呈现👇:
Node* Node::get(int key)
{int i = 0;while (i < KeyNumber){if (_keys[i] == key){return this;}if (_keys[i] > key){break;}i++;}//比key值大且为叶子节点if (this->_leaf){return nullptr;}//非叶子节点(采用递归)return _children[i]->get(key);
}
void Node::insertKey(int index,int key)
{_keys.insert(_keys.begin() + index, key);KeyNumber++;
}
void Node::insertChild(int index, Node* child)
{_children.insert(_children.begin() + index, child);
}
B树类
class BTree
{
public:BTree(int t) :_t(t), _root(new Node(t)), MAX_KEYNUMS(2 * t - 1), MIN_KEYNUMS(t - 1) { }//初始化列表
public:Node* _root;int _t;//树中最小度数const int MIN_KEYNUMS;const int MAX_KEYNUMS;
};
注意:const成员变量必须在构造函数的初始化列表中进行初始化,而不能在构造函数体内部通过赋值语句来初始化
B树类成员方法
bool contains(int _key);//判断节点是否存在
代码实现👇:
//是否存在bool contains(int _key){return _root->get(_key) != nullptr;}
B树put插入节点初实现
put实现思路:
1.首先查找本节点中的插入位置,如果没有空位(key被找到),应该走更新的逻辑
2.如果找到空位,该节点是叶子节点,可以直接插入;该节点是非叶子节点,需要继续在children[i]处继续递归插入
代码架构👇:
void BTree::doput(Node* node, int key,Node* parent,int index)
{int i = 0;while (i < node->KeyNumber){if (node->_keys[i]==key){node->_keys[i] = key;//更新return;}if (node->_keys[i] > key){break;//找到了插入位置}i++;}if (node->_leaf){node->insertKey(i, key);}else{doput(node->_children[i], key,node,i);//递归}
}
void BTree::put(int key)
{doput(_root, key,nullptr,0);
}
B树split分裂
无论哪种情况,插入完成后都可能超过节点keys数目限制,应执行节点分裂

(注:较小的数字代表索引)
void BTree::split(Node* left, Node* parent, int index)
left为待分裂节点,index是该待分裂节点作为孩子的索引
split分裂思路🎇
1.创建right节点(分裂后大于当前left节点的),把t(最小孩子数)以后的key和child都拷贝过去
2.t-1处的key插入到parent的index处(index指left作为孩子时的索引)
3.right节点作为parent的孩子插入到index+1处
分为三种情况,待分裂节点为叶子节点,非叶子节点和根节点。1️⃣我们先来实现最简单的一种情况,待分裂节点为叶子节点。
代码展示👇:
void BTree::split(Node* left, Node* parent, int index)
{
//1.创建right节点,把left中t之后的key和child移动过去Node* right = new Node(_t);right->_leaf = left->_leaf;right->_keys.assign(left->_keys.begin() + _t, left->_keys.end());right->KeyNumber = _t - 1;left->KeyNumber = _t - 1;//2.中间的key(t-1处)插入到父节点int mid = left->_keys[_t - 1];parent->insertKey(index, mid);//3.right节点作为父节点的孩子parent->insertChild(index + 1, right);
}
实现叶子节点的分裂就按照上面的思路就可以了,相对比较简单,我们现在可以小小测试一下代码,这里提供一个小的测试案例
void splitTest()//split分裂方法的测试案例
{BTree tree(2);Node* root = tree._root;root->_leaf = false;root->_keys[0] = 2;root->KeyNumber = 1;root->_children[0] = new Node(2);root->_children[0]->_keys = { 1 };root->_children[0]->KeyNumber = 1;root->_children[1] = new Node(2);root->_children[1]->_keys = { 3,4,5 };root->_children[1]->KeyNumber = 3;tree.split(root->_children[0], root, 0);
}
分裂结果可以参照小吉上面给的图
2️⃣待分裂节点为非叶子节点,这种情况就比叶子节点多一个步骤就是处理孩子
代码实现👇:
void BTree::split(Node* left, Node* parent, int index)
{
//1.创建right节点,把left中t之后的key和child移动过去Node* right = new Node(_t);right->_leaf = left->_leaf;right->_keys.assign(left->_keys.begin() + _t, left->_keys.end());//分裂节点是非叶子节点的情况if (!left->_leaf){right->_children.assign(left->_children.begin() + _t, left->_children.end());}right->KeyNumber = _t - 1;left->KeyNumber = _t - 1;//2.中间的key(t-1处)插入到父节点int mid = left->_keys[_t - 1];parent->insertKey(index, mid);//3.right节点作为父节点的孩子parent->insertChild(index + 1, right);
}
3️⃣待分裂节点为根节点,这里小吉先画个图来帮小可爱们想象一下

思路:如果parent==nullptr表示要分裂的是根节点,此时需要创建新根,原来的根节点作为新根的0(索引)孩子
代码实现👇:
if (parent == nullptr)//分裂的是根节点{Node* newRoot = new Node(_t);newRoot->_leaf = false;newRoot->insertChild(0, left);this->_root = newRoot;parent = newRoot;}
三种情况已经全部分析完了🎉🎉🎉,现在小吉把三种情况的代码进行汇总,封装在split方法中
void BTree::split(Node* left, Node* parent, int index)
{if (parent == nullptr)//分裂的是根节点{Node* newRoot = new Node(_t);newRoot->_leaf = false;newRoot->insertChild(0, left);this->_root = newRoot;parent = newRoot;}//1.创建right节点,把left中t之后的key和child移动过去Node* right = new Node(_t);right->_leaf = left->_leaf;right->_keys.assign(left->_keys.begin() + _t, left->_keys.end());//分裂节点是非叶子节点的情况if (!left->_leaf){right->_children.assign(left->_children.begin() + _t, left->_children.end());}right->KeyNumber = _t - 1;left->KeyNumber = _t - 1;//2.中间的key(t-1处)插入到父节点int mid = left->_keys[_t - 1];parent->insertKey(index, mid);//3.right节点作为父节点的孩子parent->insertChild(index + 1, right);
}
节点的分裂到这里已经全部讲完了🥳🥳🥳,下面就是要把split方法和put方法结合起来
B树put插入节点最终实现
思路:叶子节点加入key时要做分裂的检查,当节点中的有效键值数等于最大键值数时要进行节点的分裂
void BTree::put(int key)
{doput(_root, key,nullptr,0);
}void BTree::doput(Node* node, int key,Node* parent,int index)
{int i = 0;while (i < node->KeyNumber){if (node->_keys[i]==key){node->_keys[i] = key;//更新return;}if (node->_keys[i] > key){break;//找到了插入位置}i++;}if (node->_leaf){node->insertKey(i, key);}else{doput(node->_children[i], key,node,i);//递归}if (node->KeyNumber == MAX_KEYNUMS)//进行分裂{split(node, parent, index);}
}
这篇blog到这里已经全部结束了,浅浅预告一下小吉的下一篇blog讲的是B树的删除操作(又是一个大工程😶🌫️),还望大家多多支持小吉❤️
这篇博客有点长也有点难,希望小可爱们给自己多一点耐心,静下心来慢慢学,还有一定要敲代码,只有敲了代码才能发现问题。
创作不易,还望大家多多支持🌹🌹🌹(小吉写了整整3个小时🤣)
相关文章:
c++实现B树(上)
哈喽啊!好久不见,甚是想念!失踪人口要回归了,时隔一个多月小吉我终于要更新blog了🎉。在停更的一个多月中,小吉也有在好好学习提升自己,立志给大家呈现好文章。 现在让我们进入正题吧…...
【机器学习】深度强化学习–RL的基本概念、经典场景以及算法分类
引言 深度强化学习(Deep Reinforcement Learning, DRL)是机器学习的一个分支,它结合了深度学习(Deep Learning)和强化学习(Reinforcement Learning, RL)的技术 文章目录 引言一、深度强化学习–…...
【git】将本地文件上传到github
安装git 选择一个文件夹作为git仓库,cd到文件夹输入 git init文件夹出现.git文件夹,该文件夹默认为隐藏文件夹,设置为不隐藏 在cmd中输入 ssh-keygen -t rsa -C "xxxxxx.com"该邮箱为github邮箱,然后一路enter出现以…...
安卓应用开发学习:手机摇一摇功能应用尝试--摇骰子和摇红包
一、引言 前几天,我发布的日志《安卓应用开发学习:查看手机传感器信息》记录了如何查看手机传感器的信息,通过上述的方法,可以看到我的OPPO手机支持19种传感器。本篇日志就记录一下常见的加速度传感器的典型应用——“摇一摇”功…...
HTML中的<fieldset>标签元素框的使用
HTML 提供的 <fieldset> 标签用于在表单中分组相关元素。 <fieldset> 标签会在相关元素周围绘制一个框。 <legend> 标签为 fieldset 元素定义标题。 语法如下: <fieldset><legend>标题</legend><!-- 元素内容... -->…...
Linux驱动入门实验班——SR501红外模块驱动(附百问网视频链接)
目录 一、工作方式 二、接口图 三、编写思路 1.构造file_operations结构体 2.实现read函数 3.编写入口函数 4.编写中断处理函数 5.编写出口函数 6.声明出入口函数以及协议 四、源码 五、课程链接 一、工作方式 SR501人体红外感应模块有两种工作模式: …...
windows C++- Com技术简介(上)
在介绍C和winrt与COM组件技术的关系之前,有必要介绍一下com组件技术,这项技术比较古老,但是它一直作为windows的基石存在。COM 是一类独立于平台且面向对象的分布式系统,用于创建可交互的二进制软件组件。 COM 技术是 Microsoft O…...
Jenkins持续集成工具学习
一、从装修厨房看项目开发效率优化 二、持续集成工具 三、JavaEE项目部署方式对比 四、JenkinsSVN持续集成环境搭建 五、JenkinsGitHub持续集成环境搭建...
Redis:查询是否包含某个字符/字符串之三
上一篇:Redis:查询是否包含某个字符/字符串之二-CSDN博客 摘要: 遍历key,在跟进value的类型遍历value是否包含指定字符串 search_strings ,这里使用redis-py库,默认只能处理utf-8编码,如果存在…...
【Redis】数据类型详解及其应用场景
目录 Redis 常⻅数据类型预备知识基本全局命令小结 数据结构和内部编码单线程架构引出单线程模型为什么单线程还能这么快 Redis 常⻅数据类型 Redis 提供了 5 种数据结构,理解每种数据结构的特点对于 Redis 开发运维⾮常重要,同时掌握每种数据结构的常⻅…...
PARA-Drive:设计并行模型实现端到端自动驾驶
论文链接 https://openaccess.thecvf.com/content/CVPR2024/papers/Weng_PARA-Drive_Parallelized_Architecture_for_Real-time_Autonomous_Driving_CVPR_2024_paper.pdfhttps://openaccess.thecvf.com/content/CVPR2024/papers/Weng_PARA-Drive_Parallelized_Architecture_fo…...
vs2022 x64 C/C++和汇编混编 遇到的坑
vs2022 x64 C/C和汇编混编 遇到的坑 遇到的问题二、问题复现1.出错代码2.问题分析2.1 堆栈对齐问题 3.解决方案 总结奇数和偶数个寄存器的影响为什么 sub rsp, 8 对奇数个寄存器有用?结论 遇到的问题 0x00007FFFFAE24A29 (msvcp140.dll)处(位于 TestCompileConsole…...
PHP概述、环境搭建与基本语法讲解
目录 【学习目标、重难点知识】 什么是网站? 1. PHP 介绍 1.1. PHP 概述 1.1.1. PHP 是什么? 1.1.2. PHP 都能做什么? 1.2. PHP 环境搭建 1.2.1. PhpStudy 2. PHP 基本语法 2.1. PHP 语法入门 2.1.1. 第一个 PHP 程序 2.1.2. PHP …...
实现信创Linux麦克风摄像头录制(源码,银河麒麟、统信UOS)
随着信创国产化浪潮的来临,在国产操作系统上的应用开发的需求越来越多,其中一个就是需要在银河麒麟或统信UOS上实现录制摄像头视频和麦克风声音,将它们录制成一个mp4文件。那么这个要如何实现了? 一. 技术方案 要完成这些功能&a…...
深度学习9--目标检测
1.概念介绍 目标检测不仅可以检测数字,而且可以检测动物的种类、汽车的种类等。例如,自动驾驶车辆需要自动识别前方物体是车辆还是行人,需要自动识别道路两 旁的指示牌和前方的红绿灯颜色。对于自动检测的算法,有两个要求…...
第131天:内网安全-横向移动Kerberos 攻击SPN扫描WinRMWinRSRDP
案例一:域横向移动-RDP-明文&NTLM RDP利用的三种方式 1.直接在当前被控主机上进行远程连接 2.建立节点进行连接 3.端口转发,(访问当前主机的2222端口等于访问目标的3389) 第一种方式(动静太大) 直接利用被控主机进行远程连接…...
微信小程序的四种弹窗使用
在做小程序的过程中,弹窗也算是非常实用的功能了,这几天写的几个功能就用到了弹窗,也可能是初学者的问题,比较菜,想找一个可以带图片的自定义的弹窗,,这里简单介绍一下官方封装好的四个弹窗…...
我的第一个CUDA程序
MatAdd算法 实现两个矩阵对应元素相加 #include <stdio.h> #include <stdlib.h>// 矩阵加法函数 void MatAdd(int height, int width) {// 在主机内存中为 A、B 和 C 分配内存float* A (float*)malloc(height * width * sizeof(float));float* B (float*)malloc…...
workerman下的webman路由浏览器跨域的一种问题
软件版本 "php": ">7.2", "workerman/webman-framework": "^1.5.0",问题情景 使用“分组路由”做API接口前后端分离跨域,在接口测试工具调试是能正常获取数据的;但在网页浏览器上调试就遇到了CORS、404的错…...
Windows11 -MASKRCNN-部署测试
文章目录 Detectron2环境配置搭建python 环境安装Cuda \CUDNN 、PyTorch、 torchvision、cudatoolkit1、Cuda \CUDNN2、 PyTorch、 torchvision、cudatoolkit进入python测试:错误信息 3、detectron2环境在安装detecteron中,遇到报错:编译的时…...
CloudCompare进阶指南:PoissonRecon点云重建实战技巧
1. 点云重建入门:为什么选择PoissonRecon? 刚接触三维建模的朋友可能都有这样的困惑:扫描仪获取的原始点云数据看起来像一团散乱的星空,怎么才能变成光滑的曲面模型?这就是点云表面重建要解决的问题。在CloudCompare的…...
导师推荐!盘点2026年好评如潮的AI论文平台
一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文平台正在席卷学术圈,覆盖选题构思、文献综述、内容生成、降重润色与格式排版全流程,真正帮你高效搞定论文写作。 一、全流程王者:一站式搞定论文全链路&…...
别再让用户点‘拒绝‘了!微信小程序订阅消息 wx.requestSubscribeMessage 的完整避坑指南(附版本兼容代码)
微信小程序订阅消息实战:从用户拒绝到高授权率的完整策略 每次看到后台统计里那惨淡的订阅消息授权率,作为开发者的你是否感到无力?用户总是习惯性点击"拒绝",而你可能连解释的机会都没有。这不是你的代码有问题&#x…...
基于编码器-解码器神经网络的阵列综合技术复现与研究
基于编码器-解码器神经网络的阵列综合技术复现与研究 摘要 本报告旨在复现利用深度学习解决天线阵列综合问题的实验案例。传统的阵列综合方法(如Woodward-Lawson法、迭代傅里叶变换法)在面对非均匀阵列或复杂波束形状时,往往存在计算量大、依赖初始值等问题。本文构建了一…...
如何永久保存微信聊天记录?WeChatMsg免费工具终极指南
如何永久保存微信聊天记录?WeChatMsg免费工具终极指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeCha…...
软件开发中的架构:概念、价值与常见模式
在软件工程实践中,“架构”是一个高频出现但又常被误解的术语。很多人将其等同于技术选型或框架选择,但实际上,软件架构远不止于此。它关乎系统的整体结构、组件之间的关系以及指导系统演进的核心原则。本文将系统性地解释什么是软件架构、为…...
Anthropic泄露新一代Claude Mythos 模型,具备网络安全漏洞检测优势
配置错误曝光新模型Anthropic PBC 内容管理系统的一处配置错误意外泄露了其正在测试的新型大语言模型 Claude Mythos。该公司周四向《财富》杂志证实,工程师已完成该模型的训练工作,目前正与早期客户进行试点测试。Anthropic 强调这是其"迄今为止构…...
杰理之spp收发数据处理没有找到的问题处理【篇】
原因:开启#define CONFIG_APP_BT_ENABLE 宏配置后,spp的收发处理的回调默认会被库里面接管,所以在app层是看不到的。...
COMSOL报错别慌!像程序员一样‘调试’你的多物理场模型(附分步屏蔽法)
COMSOL报错别慌!像程序员一样‘调试’你的多物理场模型 面对COMSOL多物理场耦合模型报错时,许多工程师会陷入"哪里出错—如何修复"的循环焦虑。实际上,这类问题最有效的解决方式不是盲目修改参数,而是建立系统化的调试思…...
SDMatte多风格背景生成:抠图后智能匹配艺术化背景
SDMatte多风格背景生成:抠图后智能匹配艺术化背景 1. 效果亮点预览 SDMatte带来的不仅是简单的透明背景抠图。它开创性地将精准抠图与智能背景生成相结合,让每张图片都能拥有无限可能的艺术化呈现。想象一下,你的产品照片可以瞬间变成油画风…...
