C++——二叉树

引入

 map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构
 二叉搜索树的特性了解,有助于更好的理解map和set的特性

1.二叉搜索树的概念及优缺点

1.1二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

左子树的值<根的值<右子树的值

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

搜索二叉树通过二分查找可以轻松找到目标值,避免了暴力遍历的方式。

根据搜索二叉树的性质,目标值会出现在左子树,右子树则是比目标值大的值。因此,搜索二叉树查找一个值的最坏情况只需要查找树的高度次 

既然提到了最坏情况,那么搜索二叉树的时间复杂度是多少呢?

搜索二叉树的增删查改的时间复杂度实际上是O(N),因为这棵树有可能会蜕化成一个 "单边树"(也就是只往一边存,另外一边没有节点)。

 1.2二叉搜索树的部分使用及其实现

 二叉搜索树的查找

从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
最多查找高度次,走到到空,还没找到,这个值不存在。
//查找bool Find(const K& key){Node* cur = _root;//从根开始遍历,走到空说明找不到while (cur){if (cur->_key < key){//遍历的值比要找的值小,往右走cur = cur->_right;}else if (cur->_key > key){//遍历的值比要找的值大,往左走cur = cur->_left;}else{//走到这说明找到了return true;}}//while循环走到头(遇到空节点)那就说明没找到return false;}

二叉搜索树的插入

插入的具体过程如下:
树为空,则直接新增节点,赋值给root指针
树不空,按二叉搜索树性质查找插入位置,插入新节点

 

 

	bool Insert(const K& key){//为空节点的话直接插入if (_root == nullptr){_root = new Node(key);return true;}//复用一下上面的查找//走到空节点(出循环)就可以插入了//需要一个父指针,去连接那个新节点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{return false;}}//走到这说明可以插入了cur = new Node(key);//看情况把它接到父节点的左边还是右边if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}

 

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回,
否则要删除的结点可能分下面四种情 况:

要删除的结点无孩子结点

要删除的结点只有左孩子结点

要删除的结点只有右孩子结点

要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况无孩子可以与情况只有左或者只有右合并起来
因此真正的删除过程如下:
无孩子节点也就是叶子节点
删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
替换法删除:找一个能替换我的节点,交换值转换删除他
右两种替换方法:1.左子树的最大(最右)节点 2.右子树的最小(最左)节点

 

如果原本4节点还有还记记得托付给其父节点 (由于替换法删除是取左子树的最右(右子树的最左)节点,所以哪怕4节点还有子节点,那也只能有一个),比如:

 

	//删除bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;//先找到要删的那个值while (cur){if (cur->_key < key){//cur走parent也要走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 (cur == parent->_right){//cur左孩子节点为空,若cur在其父节点的右边,那么其右孩子一定大于cur的父节点//右边托付parent->_right = cur->_right;}else{//左边托付parent->_left = cur->_right;}}//托付完删除delete cur;return true;}//要删除的节点的右孩子节点为空else if(cur->_right == nullptr){if (cur == _root){//要删的值在根节点,那直接把根节点给左节点就行了_root = cur->_left;}else{if (cur == parent->_right){//若cur在其父节点的右边,那么其左孩子一定大于于cur的父节点parent->_right = cur->_left;}else{//左边托付parent->_left = cur->_left;}}//托付完删除delete cur;return true;}else{//cur左右都非空Node* rightMin = cur->_right;//右边最小节点Node* rightMinParent = cur;//右边最小节点的父节点//找右边的最小节点while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}//找到就替换一下cur->_key = rightMin->_key;//最小节点的孩子得判断是rightMin父亲的哪一边接收if (rightMin == rightMinParent->_left){rightMinParent->_left = rightMin->_right;}else{rightMinParent->_right = rightMin->_right;}delete rightMin;return true;}}}//找不到return false;}

 2.二叉搜索树的实现

2.1上述补充

节点

template<class K>
struct BSTreeNode
{typedef BSTreeNode<K> Node;Node* _left;Node* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};

二叉树的 


template<class K>
struct BSTree
{typedef BSTreeNode<K> Node;
public://强制生成默认构造BSTree() = default;BSTree(const BSTree<K>& t){_root = Copy(t._root);}Node* Copy(Node* root)//前序遍历构造(生成){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_key);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}void Destroy(Node* root)//后序遍历析构(销毁){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;}private:Node* _root = nullptr;
};

2.2查找、插入、删除递归实现

查找递归

因为无法传递根节点,所以要封装一层。

bool FindR(const K& key){return _FindR(_root, key);}bool _FindR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return true;

插入递归

注意:插入的值如何与父节点连接

可以在传(递归)的根节点上加引用,这样每深入一层,那么这层的root就是上一场root->left(right)的引用,也就是说最后一层他会自动连接新节点

	bool InsertR(const K& key){return _InsertR(_root, key);}bool _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{return false;}}

删除递归

这里的引用和上面一样

	bool EraseR(const K& key){return _EraseR(_root, key);}bool _EraseR(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else{Node* del = root;if (root->_right == nullptr){root = root->_left;}else if (root->_left == nullptr){root = root->_right;}else{Node* rightMin = root->_right;while (rightMin->_left){rightMin = rightMin->_left;}swap(root->_key, rightMin->_key);return _EraseR(root->_right, key);}delete del;return true;}}

3. 搜索二叉树的应用

3.1K模型

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
的值
比如:给一个单词word,判断该单词是否拼写正确
具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

3.2kv模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方
式在现实生活中非常常见:
比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对;
再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对

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

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

相关文章

12.4 OpenGL顶点后处理:图元裁剪

图元裁剪 Primitive Clipping Primitive Clipping&#xff08;图元裁剪&#xff09;是图形渲染管线中的一个重要步骤&#xff0c;发生在顶点处理之后、光栅化之前。这个阶段主要目的是去除位于视体&#xff08;View Volume&#xff09;之外或者被用户自定义裁剪平面&#xff0…

【Spring和Spring Boot的区别——详细讲解】

Spring和Spring Boot的区别 1. 介绍2. Spring框架3. Spring Boot4. 结论 1. 介绍 Spring和Spring Boot都是现代Java开发中常用的技术和框架&#xff0c;它们之间的关系紧密&#xff0c;Spring Boot是建立在Spring之上的&#xff0c;它简化了Spring应用的创建和开发过程。下面是…

Python中使用opencv-python进行人脸检测

Python中使用opencv-python进行人脸检测 之前写过一篇VC中使用OpenCV进行人脸检测的博客。以数字图像处理中经常使用的lena图像为例&#xff0c;如下图所示&#xff1a; 使用OpenCV进行人脸检测十分简单&#xff0c;OpenCV官网给了一个Python人脸检测的示例程序&#xff0c;…

Backtrader 文档学习- Plotting - Plotting Date Ranges

Backtrader 文档学习- Plotting - Plotting Date Ranges 1.概述 1.9.31.x版本增加了绘制部分图形的功能。 可以使用策略实例中保留完整长度的时间戳数组的索引或者使用实际的datetime.date 或datetime.datetime 实例来限制需要绘制的内容。 仍然可以使用标准的cerebro.plot…

TCP Server工具类,BIO阻塞和非阻塞NIO

启动自定义代码的方式 WebServer Initialized Event //Component//ApplicationContext context 注入//PostConstruct//AsyncEventListener(ApplicationReadyEvent.class)Componentpublic class TcpServerListener implements ApplicationListener<WebServerInitializedEven…

静态时序分析:建立时间分析

静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 在静态时序分析中&#xff0c;建立时间检查约束了触发器时钟引脚&#xff08;时钟路径&#xff09;和输入数据引脚&#xff08;数据路径&#xff09;之间的时序关系&#x…

android中实现设备尺寸适配

1、引言 设备尺寸适配的重要性想必就不用我多说了&#xff0c;在我发布的历史文章中我曾谈过Qt中的设备尺寸适配问题&#xff0c;那这里我就来教大家如何在android中做设备尺寸适配。在android中设备尺寸适配的方式有好几种&#xff0c;但我喜欢的还是使用获取设备真实尺寸然后…

MySQL基础查询篇(8)-日期和时间函数的应用

MySQL数据库是一种流行的关系型数据库管理系统&#xff0c;具有强大的日期和时间函数&#xff0c;用于对日期和时间数据进行各种操作和计算。在本篇博客中&#xff0c;我们将介绍MySQL数据库中一些常用的日期和时间函数&#xff0c;并提供详细示例说明其用法。 1. CURDATE()和…

简单介绍算法的基本概念

算法的基本概念 计算机所进行的一切操作都是由程序决定的&#xff0c;程序是由人们事先编好并输入计算机的。 一个程序包括以下两方面的内容: 对数据的描述&#xff1a;在程序中要指定用到哪些数据以及这些数据的类型和数据的组织形式&#xff0c;即数据结构 对操作的描述&a…

c语言游戏实战(4):人生重开模拟器

前言&#xff1a; 人生重开模拟器是前段时间非常火的一个小游戏&#xff0c;接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏&#xff1a; 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 &#xff08;1&#xff09; 游戏开始的时…

Transformer实战-系列教程10:SwinTransformer 源码解读3(SwinTransformerBlock类)

&#x1f6a9;&#x1f6a9;&#x1f6a9;Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 SwinTransformer 算法原理 SwinTransformer 源码解读1&#xff08;项目配置/SwinTr…

PLC在物联网中位置—承上启下,与上位机下位机的关联。

谈到物联网&#xff0c;就绕不开PLC&#xff0c;本文着重介绍PLC的定义、与单片机的区分&#xff0c;价值、物联网中的位置&#xff0c;以及和上位机、下位机的关联&#xff0c;让友友们对PLC有个全面的认知。 一、什么是PLC PLC是可编程逻辑控制器&#xff08;Programmable L…

UI自动刷新大法:DataBinding数据绑定

之前我们讲了DataBinding在Activity、Fragment、RecyclerView中的基础使用&#xff0c;而那些常规使用方法里&#xff0c;每当绑定的变量发生数据变化时&#xff0c;都需要ViewDataBinding重新设值才会刷新对应UI。而DataBinding通过内部实现的观察者模式来进行自动刷新UI&…

go消息队列RabbitMQ - 订阅模式-direct

1.发布订阅 在Fanout模式中&#xff0c;一条消息&#xff0c;会被所有订阅的队列都消费。但是&#xff0c;在某些场景下&#xff0c;我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。 在Direct模型下&#xff1a; 队列与交换机的绑定&#xff0c;不能…

第 384 场 LeetCode 周赛题解

A 修改矩阵 模拟 class Solution { public:vector<vector<int>> modifiedMatrix(vector<vector<int>> &matrix) {int m matrix.size(), n matrix[0].size();vector<int> mx(n, INT32_MIN);for (int i 0; i < m; i)for (int j 0; j &l…

Java微服务学习Day1

文章目录 认识微服务服务拆分及远程调用服务拆分服务远程调用提供者与消费者 Eureka注册中心介绍构建EurekaServer注册user-serviceorder-service完成服务拉取 Ribbon负载均衡介绍原理策略饥饿加载 Nacos注册中心介绍配置分级存储负载均衡环境隔离nacos注册中心原理 认识微服务…

ChatGPT学习大纲

引言 在2023年2月份左右开始使用ChatGPT时&#xff0c;就被它强大的理解能力和应答效果所折服&#xff0c;这期间一直在断断续续的学习和使用&#xff0c;也没形成一个完整的学习过程&#xff0c;最近刚好有空&#xff0c;就寻思着好好再学习总结一下&#xff0c;故写出了ChatG…

Python : 使用python实现学生管理系统的功能,详细注释

一、学生管理系统 学生描述&#xff1a;姓名、年龄、成绩 学生管理系统功能&#xff1a;添加学生信息、删除学生信息、根据姓名修改学生信息、根据姓名查询学生信息、显示所有学生信息、退出系统 二、代码说明 1. 将每一个学生的信息放一个元组中&#xff0c;再把元组添加到列表…

java中使用Lambda表达式实现参数化方法

Lambda表达式实现参数化方法说明 Lambda表达式在Java中是一种简洁、函数式的方式来表示匿名函数。它们特别适用于那些需要一个函数作为参数的方法&#xff0c;即函数式接口。参数化方法&#xff08;通常指的是泛型方法&#xff09;是那些可以接受类型参数的方法&#xff0c;这…

2.3 Verilog 数据类型

Verilog 最常用的 2 种数据类型就是线网&#xff08;wire&#xff09;与寄存器&#xff08;reg&#xff09;&#xff0c;其余类型可以理解为这两种数据类型的扩展或辅助。 线网&#xff08;wire&#xff09; wire 类型表示硬件单元之间的物理连线&#xff0c;由其连接的器件输…