二叉树搜索树(下)

二叉树搜索树(下)

在这里插入图片描述

二叉搜索树key和key/value使用场景

key搜索场景

只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断 key在不在。key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。

场景1:⼩区无⼈值守⻋库,⼩区⻋库买了⻋位的业主⻋才能进⼩区,那么物业会把买了⻋位的业主的⻋牌号录⼊后台系统,⻋辆进⼊时扫描⻋牌在不在系统中,在则抬杆,不在则提⽰⾮本⼩区⻋辆,⽆法进⼊。

场景2:检查⼀篇英⽂⽂章单词拼写是否正确,将词库中所有单词放⼊二叉搜索树,读取文章中的单词,查找是否在二叉搜索树中,不在则波浪线标红提示。

可以通俗地说,只有key作为关键码,就是判断在不在的问题。

key/value搜索场景

每⼀个关键码key,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字⾛二叉搜索树的规则进⾏⽐较,可以快速查找到key对应的value。key/value的搜索场景实现的二叉树搜索树⽀持修改,但是不⽀持修改key,修改key破坏搜索树结构了,可以修改value

场景1:简单中英互译字典,树的结构中(结点)存储key(英⽂)和vlaue(中⽂),搜索时输⼊英⽂,则同时查找到了英⽂对应的中⽂。

场景2:商场⽆⼈值守⻋库,⼊⼝进场时扫描⻋牌,记录⻋牌和⼊场时间,出⼝离场时,扫描⻋牌,查找⼊场时间,⽤当前时间-⼊场时间计算出停⻋时⻓,计算出停⻋费⽤,缴费后抬杆,⻋辆离场。

场景3:统计⼀篇⽂章中单词出现的次数,读取⼀个单词,查找单词是否存在,不存在这个说明第⼀次出现,(单词,1),单词存在,则++单词对应的次数。

我们可以用命名空间将我们之前写的只有key的搜索二叉树的代码进行隔离,再用另一个命名空间将接下来要写的key/value二叉搜索树的代码进行隔离。

参考代码

namespace key
{//之前写的只有key的二叉搜索树结构和方法定义代码
}
namespace key_value
{template<class K,class V>struct BSTNode{K _key;V _value;BSTNode<K,V>* _left;BSTNode<K,V>* _right; BSTNode(const K& key,const V& value):_key(key),_value(value), _left(nullptr), _right(nullptr){}};template<class K,class V>class BSTree{using Node = BSTNode<K,V>;public:bool Insert(const K& key,const V& value){if (_root == nullptr){_root = new Node(key,value);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}elsereturn false;//避免冗余}//到这里说明cur已经找到空位置了,但是不知道是从右走的还是往左走的,得再比一次cur = new Node(key,value);if (key < parent->_key){parent->_left = cur;}else{parent->_right = cur;}return true;}void InOrder(){_InOrder(_root);cout << endl;}Node* Find(const K& key)//不再是返回布尔值{Node* cur = _root;while (cur){if (key < cur->_key){cur = cur->_left;}else if (key > cur->_key){cur = cur->_right;}else{return cur;//返回结点指针,方便以后修改value}}return nullptr;}bool Erase(const K& key){Node* cur = _root;Node* parent = nullptr;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{if (cur->_left == nullptr){//父为空,也就是说要删的是头结点if (parent == nullptr){_root = cur->_right;}else{//要判断父节点的左还是右指针指向cur的孩子if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;return true;}//cur的右孩子为空,把cur的左孩子给父else if (cur->_right == nullptr){if (parent == nullptr){_root = cur->_left;}else{//判断父节点的左还是右指针指向cur孩子if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}else//现在是cur的左右孩子都不为空{//第一步,找R(这里采用cur的右子树的最小结点/也就是最左结点)Node* p = cur;//这里如果初始给空,后面如果不进循环,会造成对空指针的解引用Node* r = cur->_right;while (r->_left){p = r;r = r->_left;}//第二步,进行交换(但不用真的把cur的值再去给r)cur->_key = r->_key;//第三步,删除R——很容易考虑不全面!!删除R又要把删除的问题考虑全面,但这里不能用递归,会找不到//在删除R时要把R的右孩子(只可能是右孩子)给给父,但是父的左还是右指针不确定if (p->_right == r){p->_right = r->_right;}else{p->_left = r->_right;}delete r;return true;}}}return false;}private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}Node* _root = nullptr;};
}
词典

然后我们再通过一段程序来测试一下key_value版本二叉搜索树的代码是否有误:

key_value::BSTree<string, string> dict;
dict.Insert("abandon", "抛弃");
dict.Insert("mania", "狂热");
dict.Insert("disc", "唱片");
dict.Insert("vain", "徒劳");string str;
while (cin >> str)
{auto ret = dict.Find(str);if (ret){cout << "->" << ret->_value << endl;}else{cout << "无此单词,请重新输入" << endl;}
}

这段程序里,我们通过key_value版本的二叉搜索树创建除了一个词典,然后while(cin>>str)里,cin>>str的返回值是istream类型的,可以通过operator bool转化成布尔类型(大致如此)。

总之,效果是我们可以输入多个(个数不确定)string类型的数据,然后程序在这棵树(这本词典)里查找,如果找到了,就把它的value值也就是中文名打印出来,如果找不到会提示,想要退出就按ctrl+z(相当于给流设置了一个错误标志)。

统计次数

然后我们再来一段程序,来用key_value版本的二叉搜索树进行次数的统计:

string arr[] = {"apple","melon","apple","melon","apple","apple","melon","banana","apple","banana"};
key_value::BSTree<string, int> countTree;for (const auto& str : arr)//通过使用引用,减少赋值消耗
{//先查找水果在不在搜索树//1.不在,说明水果第一次出现,则插入<水果,1>//2.在,则查找到的结点中水果对应的次数++auto ret = countTree.Find(str);if (ret == __nullptr){countTree.Insert(str, 1);}else{//修改valueret->_value++;}
}
countTree.InOrder();

搜索二叉树的拷贝问题

key_value::BSTree<string, int> copy = countTree;
copy.InOrder();

可以看到,copy和countTree里面的_root地址是一样的,说明是浅拷贝,而浅拷贝析构时会出问题(崩溃),我们需要的是深拷贝。

搜索树的析构有很多种写法,可以用循环去层序遍历地析构,但比较麻烦(析构一层的时候还得保存它的孩子)。

递归析构会简单一些。

public:~BSTree(){Destroy(_root);_root = nullptr;//别忘了}private:void Destroy(Node* root){if (root == nullptr)return;//后序析构Destroy(root->_left);Destroy(root->_right);delete root;}

这样写完析构之后,我们可以看看,在这样浅拷贝的情况下,析构时会崩溃:

要实现深拷贝,这里借助拷贝构造不好搞。

老老实实使用前序进行拷贝。

一个个插入不行,如果使用中序遍历一个个插入,形状就变了。

BSTree(const BSTree& t)
{_root = Copy(t._root);
}Node* Copy(Node* root)
{if (root == nullptr)return nullptr;//前序Node* newRoot = new Node(root->_key, root->_value);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;
}

这时我们还有一个报错:没有合适的默认构造可以用。因为默认构造生成的前提是我们没有写任何的构造,而现在我们写了拷贝构造。

C++11为我们提供了关键字default:

BSTree() = default;

然后是赋值重载,这我们就可以考虑用现代写法了:

BSTree& operator=(BSTree tmp)
{swap(_root, tmp._root);return *this;
}

这里我们将要赋的值传值传参给tmp,传值传参会调用拷贝构造,tmp就是我们要的,我们将其root与 *this的root直接交换,在这个函数结束后tmp会自动销毁,把tmp现在的root也就是 *this原本的root带走。最后我们选择返回引用类型,减少消耗。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

人力资源招聘系统-提升招聘效率与质量的关键工具

在当今这个竞争激烈的商业环境中&#xff0c;企业要想在市场中立于不败之地&#xff0c;关键在于拥有高素质的人才队伍。然而&#xff0c;传统的招聘方式往往效率低下&#xff0c;难以精准匹配企业需求与人才特质&#xff0c;这无疑给企业的发展带来了不小的挑战。 随着科技的飞…

【C++】类中的“默认成员函数“--构造、析构、赋值

目录 概念引入&#xff1a; 一、构造函数 问题引入&#xff1a; 1&#xff09;构造函数的概念 2&#xff09;构造函数的特性 二、析构函数 1&#xff09;析构函数概念 2&#xff09;析构函数特性 三、拷贝构造函数 1)拷贝构造函数概念 示例代码&#xff1a; 2)深拷…

环丙烷环辛炔聚乙二醇磷脂,淡黄色固体,BCN-PEG-DSPE

中文名称&#xff1a;环丙烷环辛炔聚乙二醇磷脂 英文名称&#xff1a;BCN-PEG-DSPE 外观&#xff1a;通常为黄色或淡黄色固体 材料来源&#xff1a;为华生物 溶解性&#xff1a;在有机溶剂&#xff08;如氯仿、乙醇&#xff09;中具有良好的溶解性&#xff0c;而在水中的溶…

202409电子学会青少年机器人技术等级考试(六级)理论综合真题

青少年机器人技术等级考试理论综合试卷&#xff08;六级&#xff09; 分数&#xff1a; 100 题数&#xff1a; 30 一、 单选题(共 20 题&#xff0c; 共 80 分) 1. 使用 ESP32 for Arduino SPI 类库&#xff0c; 下列选项中&#xff0c; 具有设置时钟模式功能的成员函数是&…

如何学习VBA_3.2.14:字符串的处理

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的劳动效率&#xff0c;而且可以提高数据处理的准确度。我推出的VBA系列教程共九套和一部VBA汉英手册&#xff0c;现在已经全部完成&#xff0c;希望大家利用、学习。 如果…

ABeam News | ABeam中国受邀参加2024中国知识管理年会暨第14届China MIKE颁奖典礼,并荣获大奖

“ABeam/ News ” 近日&#xff0c;2024中国知识管理年会暨第14届China MIKE颁奖典礼圆满召开&#xff0c;大会结合AI赋能新质生产力的热点话题&#xff0c;以“AI超能力KM新价值” 作为主题&#xff0c;为与会观众带来知识管理的一场盛宴。ABeam中国受邀参会并荣获2024 China…

Error: Could not find or load main class org.apache.catalina.startup.Bootstrap

#现象&#xff1a; 官网下载tomcat source包后&#xff0c;启动报错&#xff0c;等一系列缺包造成服务无法启动 Error: Could not find or load main class org.apache.catalina.startup.Bootstrapjava.lang.ClassNotFoundException: org.apache.juli.logging.LogFactory原因 …

论文解读《CTRLsum: Towards Generic Controllable Text Summarization》

引言&#xff1a;一篇上交大佬的著作 ✅ NLP 研 2 选手的学习笔记 笔者简介&#xff1a;Wang Linyong&#xff0c;NPU&#xff0c;2023级&#xff0c;计算机技术 研究方向&#xff1a;文本生成、大语言模型 论文链接&#xff1a;https://aclanthology.org/2022.emnlp-main.396.…

【spotfire】脚本相关

文章目录 ironpython脚本使用JS实现弹出窗口思路实现效果 脚本的使用可以极大扩展spotfire的功能&#xff0c;但如何使用脚本一直不得其门而入&#xff0c;咨询厂商、查询资料&#xff0c;特此记录备忘。 ironpython脚本使用 参见官网教程&#xff1b; 部分参考资料如下&#…

嵌入式硬件杂谈(一)-推挽 开漏 高阻态 上拉电阻

引言&#xff1a;对于嵌入式硬件这个庞大的知识体系而言&#xff0c;太多离散的知识点很容易疏漏&#xff0c;因此对于这些容易忘记甚至不明白的知识点做成一个梳理&#xff0c;供大家参考以及学习&#xff0c;本文主要针对推挽、开漏、高阻态、上拉电阻这些知识点的学习。 目…

RCAgent:云故障根因分析的自主智能体工具增强型大模型

人工智能咨询培训老师叶梓 转载标明出处 由于云上计算部署的不断扩展&#xff0c;手动在线异常RCA工作流程&#xff0c;如创建故障排除工具&#xff0c;常常使网站可靠性工程师&#xff08;SRE&#xff09;应接不暇。为了提高云服务可靠性效率&#xff0c;一系列人工智能运维&…

PET-文件包含-FINISHED

include发生错误报warning&#xff0c;继续执行。require发生错误直接error&#xff0c;不继续执行 无视扩展名&#xff0c;只要能解析&#xff0c;就能当可执行文件执行&#xff0c;哪怕文件后缀或没后缀 1 条件竞争 pass17 只需要知道tmp的路径。把xieshell.jpg上传&…

基于Java+SpringBoot+Vue前后端分离课程管理系统

一、作品包含 源码数据库设计文档全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&…

学术界的秘密武器:Zotero7大插件推荐

还在为海量文献管理头疼吗?还在为找不到合适的插件犯愁吗?别急,今天我就要带你解锁Zotero的终极武器 - 那些让你爱不释手的必备插件! 作为一个从小白到文献管理达人的过来人,我可以负责任地说:没有这些插件,你的Zotero只能发挥一半功力!安装了这些插件,你的效率绝对能飙升! …

Linux·进程信号

信号是一种用户、OS、其他进程&#xff0c;向目标进程发送异步事件的一种方式。 在系统中信号是OS出场时程序员就内置好了的&#xff0c;因此任何进程都认识所有信号&#xff0c;信号产生之前&#xff0c;信号的处理方案就已经设定好了&#xff0c;一般有三种 1. 默认行为 2.…

BizDevOps:从理念到实践,贯通企业全链路协同

&#x1f446; 点击蓝字 关注我们 引言 BizDevOps的概念由DevOps发展和进化而来&#xff0c;其目标超越了开发和运维的协同&#xff0c;进一步实现业务、研发和运维的全链条协作&#xff0c;让业务作为价值的起点及核心目标。 BizDevOps的核心驱动力在于解决效率和正确性上的割…

工厂方法模式和抽象工厂模式

序 本文主要是记录学习设计模式当中的工厂方法和抽象工厂时碰到的疑惑和对答案的探讨 刚接触时的工厂方法模式和抽象工厂模式 工厂方法模式 类图 代码 //工厂public interface TVFactory {TV produce(); }public class TclTVFactory implements TVFactory{Overridepublic T…

NVR小程序接入平台EasyNVR多品牌NVR管理工具/设备:RTMP协议摄像头的接入

随着安防技术的不断进步&#xff0c;越来越多的摄像头开始支持RTMP&#xff08;Real Time Messaging Protocol&#xff09;协议&#xff0c;这种协议使得视频流的实时传输和分发变得更加高效和便捷。NVR小程序接入平台EasyNVR作为一款功能强大的流媒体服务器&#xff0c;支持多…

硬件基础20 数模转换器D/A DAC

目录 一、DAC基本原理 二、倒T形电阻网络D/A转换器 三、权电流型D/A转换器 四、重要技术指标与参数 1、分辨率/位数 2、转换精度 &#xff08;1&#xff09;、比例系数误差 &#xff08;2&#xff09;、失调误差 3、转换速度 4、温度系数 五、DAC的应用 1、数字式可…

Memory consistency model 梳理目录

(图片来源&#xff1a;https://mp.weixin.qq.com/s/uz4fZgJSRNm-MIRdXgBMmw) 闲聊内存模型(Memory Model)https://blog.csdn.net/zhangshangjie1/article/details/143743250?sharetypeblogdetail&sharerId143743250&sharereferPC&sharesourcezhangshangjie1&…