C++之二叉树进阶|搜索树|key/value模型

news/2024/5/8 23:03:27/文章来源:https://blog.csdn.net/weixin_51568389/article/details/126677309

在这里插入图片描述

🐧主页详情:Choice~的个人主页
📢作者简介:🏅物联网领域创作者🏅 and 🏅阿里专家博主🏅 and 🏅华为云享专家🏅
✍️人生格言:最慢的步伐不是跬步,而是徘徊;最快的脚步不是冲刺,而是坚持。
🧑‍💻人生目标:成为一名合格的程序员,做未完成的梦:实现财富自由。
🚩技术方向:NULL
🀄如果觉得博主的文章还不错的话,请三连支持一下博主哦
💬给大家介绍一个我一直在用的求职刷题收割offe👉点击进入网站

文章目录

  • 二叉搜索树
    • 中序遍历(Inorder)
    • 插入节点
    • 查找节点
    • 删除节点:
    • key/value模型
      • 实现中英文翻译
      • 翻译效果
    • 修改节点

二叉搜索树

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

image-20220830090331348

int a [] = {5,3,4,1,7,8,2,6,0,9};

使用价值:搜索

template <class K>//为了统一类型

二叉树包含左子树和右子树和值:并且希望哪里都可以访问,我们定义一个结构体struct BSTNode默认是公有访问

template <class K>
struct BSTNode
{BSTNode<K>*_right;BSTNode<K> *_left;K _key;};

然后开始创建对象class BSTree,为了方便typedef BSTNode Node * _root;简写成typedef BSTNode<K> Node; Node* _root;整体框架:

class BSTree
{typedef BSTNode<K> Node;public:BSTree() :_root(nullptr){}
private:Node* _root;};

增删查改接口:

void InOrder(){}//中序遍历
bool Insert(const K&key){}
Node*find(const K&key){}
bool Erase(const K &key){}

中序遍历(Inorder)

这里为了可以在类里面访问私有成员变量,我们写一个方法把_root传参_:

void InOrder(){_InOrder(_root);cout << endl;}
void _InOrder(Node*root){if (root == nullptr)return;//中序遍历_InOrder(root->_left);cout << root->_key<< " ";_InOrder(root->_right);}

根据完全二叉树的特性,可以把数组5, 3, 4, 1, 7, 8, 2, 6, 0, 9构成下图逻辑结构:

image-20220830164235762

中序的遍历规则是:先遍历左孩子然后是父亲最后是右孩子

插入节点

插入第一个节点,根节点现在为空,判断一下,如果是第一次插入就是根节点:

if (_root == nullptr){_root = new Node(key);return true;}

这里用了new 开辟空间,需要自己写一个构造函数并初始化

BSTNode(const K& key):_right(nullptr), _left(nullptr), _key(key){}

我们需要有个节点cur指向根节点Node* cur = _root;

对cur指向的值进行判断是否比插入的节点小,根据二叉搜索树特性,左孩子小于右孩子;如果cur指向的值比新插入的小,那我们cur指向右孩子cur = cur->_right;,如果cur指向的值比新插入的大,那我们cur指向左孩子cur = cur->_left;,最后一种就是它们相等,那就返回假;

while(cur)if (cur->_key < key){cur = cur->_right;}else if (cur->_key>key){	cur = cur->_left;}else{return false;}
}

左孩子和右孩子已经分出来了,那结合起来的它们父亲该谁做呢?我们就需要定义一个父亲:Node*parent = nullptr;,并判断父亲指向的值是大还是小

Node* cur = _root;Node*parent = nullptr;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key>key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key)parent->_right = cur;else {parent->_left = cur;}return true;

查找节点

查找很简单,通过比大小,大的在右边,小的在左边:

Node*find(const K&key){Node*cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn cur;}return NULL;}

删除节点:

我们删除节点需要找到那个节点,那怎么找呢?这里我们也和插入一样使用用双指针,通过cur指向的节点可以找到父亲和左右孩子,但是删除有3种情况:

image-20220830171457477

解决方案:

1:删除自己,父亲指向自己位置的指针置空

2:删除节点,把孩子交给父亲,顶替自己的位置

3:替换法删除。去孩子里面找一个值能替换自己位置的节点,替换自己删除

image-20220830171805280

image-20220830172129685

第一种情况:删除8,我们需要通过8这个值找到父亲,并判断父亲指向这个值是左孩子还是右孩子,看图8这个父亲有个右孩子9,删除8之后,右孩子8的父亲也要指向右孩子9

image-20220830172651289

第二种就是相反:

第三种就需要找删除树的最小的节点,我们假设在右子树,我们也定义两个指针,一个是最小值的父亲和最小值的右子树Node* minParent = cur;Node* minRight = cur->_right;,当然最小的一个节点一定是在最左边

while (minRight->_left)
{minRight = minRight->_left;
}

找到最小的值后 和删除的值替换

cur->_key = minRight->_key;//替换
//删除替换节点
if (minParent->_left == minRight)//如果父亲有左孩子minParent->_left = minRight->_right;
else//如果是右孩子minParent->_right = minRight->_right;
delete minRight;

删除测试:

image-20220830173756616

但是仔细一看会有一个bug:那就是全部删除之后,我们没有处理为空树的情况会怎么样;

image-20220830174334351

我们需要在前面判断,要是删除到根节点,就让根节点指向左子树还是右子树

image-20220831074534470

//找到了,准备删除if (cur->_left == nullptr)//左为空{if (cur == _root){_root=cur->_right;//指向右树}else{if (parent->_left == cur){parent->_left = cur->_right;}elseparent->_right = cur->_right;}delete cur;}else if (cur->_right == nullptr)//右为空{if (cur == _root){_root = cur->_left;//指向左树}else{if (parent->_left == cur){parent->_left = cur->_left;}elseparent->_right = cur->_right;}

key/value模型

image-20220831083633539

我们再加一个模板class V

template <class K,class V>

实现中英文翻译

template <class K,class V>
struct BSTNode
{BSTNode<K,V>*_right;BSTNode<K,V> *_left;K _key;V _value;BSTNode(const K& key,const V& value):_right(nullptr), _left(nullptr), _key(key), _value(value){}
};
template <class K,class V>
class BSTree
{typedef BSTNode<K,V> Node;public:BSTree() :_root(nullptr){}Node* Insert(const K&key,const V&value){if (_root == nullptr){_root = new Node(key,value);return nullptr;}Node* cur = _root;Node*parent = nullptr;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key>key){parent = cur;cur = cur->_left;}else{return nullptr;}}cur = new Node(key,value);if (parent->_key < key)parent->_right = cur;else parent->_left = cur;return cur;}Node*find(const K&key){Node*cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn cur;}return NULL;}bool Erase(const K &key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key>key){parent = cur;cur = cur->_left;}else{//找到了,准备删除if (cur->_left == nullptr)//左为空{if (cur == _root){_root=cur->_right;//指向右树}else{if (parent->_left == cur){parent->_left = cur->_right;}elseparent->_right = cur->_right;}delete cur;}else if (cur->_right == nullptr)//右为空{if (cur == _root){_root = cur->_left;//指向左树}else{if (parent->_left == cur){parent->_left = cur->_left;}elseparent->_right = cur->_right;}}else{//找到右子树的最小节点Node* minParent = cur;//这里不能等于nullprt,当cur的右节点没有左孩子,循环不进去,删除替换节点就会出错Node* minRight = cur->_right;while (minRight->_left){minRight = minRight->_left;}cur->_key = minRight->_key;//替换//删除替换节点if (minParent->_left == minRight)//如果父亲有左孩子minParent->_left = minRight->_right;else//如果是右孩子minParent->_right = minRight->_right;delete minRight;}return true;}}return false;}void _InOrder(Node*root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key<< " ";_InOrder(root->_right);}void InOrder(){_InOrder(_root);cout << endl;}private:Node* _root=nullptr;};

翻译效果

image-20220831083304794

修改节点

知道为什么才开始修改节点吗?因为我们可以通过K/V模型修改,Key是不能修改的;假设修改根节点值为0,那树的结构全乱了,但是有了value值后可以对它进行修改,

  • 如果对大家有帮助,请三连支持一下!
  • 有问题欢迎评论区留言,及时帮大家解决!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_5159.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线程与进程的关联

上篇博客 我们说了进程 下面我来用一个我们回忆一下 其实啊 进程在频繁的创建 / 销毁的时候 是非常低效的 -因为创建的时候 要给进程分配资源(内存/文件) 赋值到CPU上 是一个大活 所以 有了线程 那咱们已经很了解进程了 直接说 线程 与 进程 的区别: 对比进程线程1包含线程2…

微服务项目:尚融宝(14)(前端平台:尚融宝管理系统路由配置)

认清现实&#xff0c;放弃幻想&#xff0c;准备斗争 一、组件定义 1、创建vue组件 在src/views文件夹下创建以下文件夹和文件 2、core/integral-grade/list.vue <template><div class"app-container">积分等级列表</div> </template> 3、…

文章组合生成-免费文章组合生成软件

文章组合生成软件&#xff0c;今天给大家分享一款免费的文章组合工具&#xff0c;自动从组文章生成段落目录详细参考图片。 网络的速度让一切的信息都是尽可能快的传达&#xff0c;为了给用户供给新鲜的信息&#xff0c;搜索引擎也是不断的增加抓取内容的频率&#xff0c;但是蜘…

设计模式-概述. 类图.软件设计原则详细讲解

1.1 软件设计模式的产生背景 "设计模式"最初并不是出现在软件设计中&#xff0c;而是被用于建筑领域的设计中。 1990年软件工程界开始研讨设计模式的话题&#xff0c;后来召开了多次关于设计模式的研讨会。直到1995 年&#xff0c;艾瑞克伽马&#xff08;ErichGamm…

kafka原理解读

一、Kafka Kafka是一个分布式的消息系统。 二、解决问题 消息系统通常被应用于异步处理、应用解耦、流量削峰、消息通信等场景。 异步处理 生产者将消息写入消息队列中&#xff0c;消费者异步拉取消息队列消息&#xff0c;从而提升消息处理能力。 应用解耦 Kafka作为消息传递…

【Linux操作系统】-- 多线程(三)-- 线程池+单例模式+读写者模型

目录 线程池 场景 代码实现 线程安全的单例模式 懒汉实现方式和懒汉实现方式 饿汉方式实现单例模式 懒汉方式实现单例模式 实战代码演练单例模式 读者写者模型 解释 基本操作 创建/销毁读写锁 读者锁和写者锁 解锁 伪代码理解读写锁 优先级 挂起等待锁vs自旋锁…

关于我在字节跳动青训营做了个抖音这件事

一、实践介绍 1.1项目核心信息 本项目实现了影视综艺榜单及其历史数据查询&#xff0c;实现个人页面展示、个人页面粉丝和关注列表、个人页面已发布视频列表及其详情页 1.2项目服务地址 https://github.com/gujunhe/douyin 1.3GitHub地址 https://github.com/gujunhe/dou…

centos8同步时间安装时间校准服务

多余的话都写在教程的后面&#xff0c;直接进入下面的操作命令。下面所有的操作都必须使用root账户来操作。切记。 #1. 查看当前时间 date#2. 添加wlnmp源 rpm -ivh http://mirrors.wlnmp.com/centos/wlnmp-release-centos.noarch.rpm#3. 安装ntp服务 yum install wntp#4. 时间…

Python爬虫之Js逆向案例(10)-爬虫数据批量写入mysql数据库

最近收到小伙伴们的私信&#xff0c;说如何将爬取的数据批量存到数据库中&#xff1f;数据入库也是童鞋们必须掌握的技能&#xff01;数据回来之后&#xff0c;肯定需要存放&#xff0c;实效高、数量少的可能大多存放在cvs文件中&#xff0c;通常情况都是要存放到数据库的&…

[JS入门到进阶] 7条关于 async await 的使用口诀,新学 async await?背10遍,以后要考!快收藏

我是HullQin&#xff0c;公众号线下聚会游戏的作者&#xff08;欢迎关注公众号&#xff0c;发送加微信&#xff0c;交个朋友&#xff09;&#xff0c;转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》&#xff0c;是个网页&#xff0c;可以很方便的跟朋友联机玩…

蓝牙音响插着电源线就会一直有电流声怎么回事呢 All In One

蓝牙音响插着电源线就会一直有电流声怎么回事呢 All In One蓝牙音响插着电源线就会一直有电流声怎么回事呢 All In One周围存在电源的电磁干扰 ✅之前使用 USB 集线器的旁边上有一个电源插板,估计是收到了电磁干扰了 ❌直接使用电脑自带的 USB 接口连接即可 🚀refs https://…

软件测试概念总结

软件测试1.软件测试&#xff1a;2.软件测试的特点&#xff1a;3.软件测试和开发的区别&#xff1a;4.软件测试与调试的区别&#xff1a;5.优秀的软件测试人员具备的素质6.核心竞争力7.学习方法8.学习内容9.需求的概念10.用户需求11.软件需求12.生成测试用例的过程13.为什么需求…

GO语言自学_001_环境配置_windowx11_x64版本

GO语言自学_001_环境配置_windowx11_x64版本下载地址: https://golang.google.cn/ 1、看到那个下载按钮了么?点她!2、点击download到这个页面,根据电脑自身系统配置下载包。3、下载完毕后,运行.msi文件,一路next就可以了。本人电脑默认下载到C:\Program Files\Go路径。需要…

创建员工表格,遍历数组获取每个员工,并且渲染到表格中

首先是CSS部分,根据需求添加属性,可以调整 再是盒子部分 接下来是js部分:重点就是JS部分,利用遍历数组获取每个员工,再进行渲染,注意for下面的console.log( ` 这里面有一个标点符号千万别忘记(叫反引号 是 Shrit +ESC下面这个键) ` ) 实际效果图

计算机毕业设计springboot+vue基本微信小程序的外卖点餐订餐平台

项目介绍 餐饮行业是一个传统的行业。根据当前发展现状&#xff0c;网络信息时代的全面普及&#xff0c;餐饮行业也在发生着变化&#xff0c;单就点餐这一方面&#xff0c;利用手机点单正在逐步进入人们的生活。传统的点餐方式&#xff0c;不仅会耗费大量的人力、时间&#xf…

SAP云集成 SAP Integration Suite启用过程,踩坑记

第一步 &#xff1a;创建一个 subscription I现在访问&#xff0c;会提示unauthorized&#xff0c;无权访问 配置了这个&#xff0c;还是无法访问 CPI界面 最后在CPI 官方文档中看到这么一段&#xff0c;tricky&#xff0c;清除浏览器缓存和cookie 然后进来了。。。 第二步&am…

[Latex] \bibitem{} | .bbl 格式参考文献转换与获得

BibTex格式&#xff0c;在dblp或者谷歌学术等都可直接获得&#xff0c;但是\bibitem{}无法直接获得&#xff0c;因此需要通过BibTex格式进行转换。 BibTeX格式参考文献&#xff1a; \bibitem{}格式参考文献&#xff1a; 将BibTeX格式转为\bibitem{}格式 准备好2个文件&…

【Word】如何批量导出ppt中的备注

【Word】如何批量导出ppt中的备注文件 | 导出 | 创建讲义 | 备注在幻灯片旁在word中删除左边两列,复制剩下的表格 | 粘贴-只保留文本

解决 Element的el-input 密码输入框浏览器自动填充账号密码问题

问题描述 通常情况下&#xff0c;浏览器会默认将已保存的账号密码 填充到 input type 值为password的输入框内&#xff0c;如果在登录页面&#xff0c;这当然是非常好的&#xff0c;自动填充密码可以节约时间&#xff0c;提高良好的使用体验&#xff0c;这样当然是没有什么问…

Spring Cloud Gateway 网关整合 Knife4j

文章目录1&#xff1a;环境准备2&#xff1a;gateway服务设置1&#xff1a;导包2&#xff1a;yml配置3&#xff1a;添加配置类&#xff0c;从网关服务中获取服务列表4&#xff1a;重写并覆盖/swagger-resources接口3&#xff1a;其他业务逻辑服务设置1&#xff1a;其他服务导包…