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.mfbz.cn/a/380836.html

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

相关文章

网络编程-Socket套接字

目录 1.网络编程 1.1定义与图解 1.2基本概念 &#xff08;1&#xff09;发送端和接收端 &#xff08;2&#xff09;请求和响应 &#xff08;3&#xff09;客户端和服务端 2.Socket套接字 2.1定义 2.2分类 &#xff08;1&#xff09;流套接字 &#xff08;2&#xff…

无人机图像识别技术研究及应用,无人机AI算法技术理论,无人机飞行控制识别算法详解

在现代科技领域中&#xff0c;无人机技术是一个备受瞩目的领域。随着人们对无人机应用的需求在不断增加&#xff0c;无人机技术也在不断发展和改进。在众多的无人机技术中&#xff0c;无人机图像识别技术是其中之一。 无人机图像识别技术是利用计算机视觉技术对无人机拍摄的图像…

黄金交易策略(Nerve Knife):反趋势锁定单的处理机制

锁定单是由大趋势反转之后原来的趋势单转变而来的&#xff0c;会在趋势再次反转时变为趋势单或者转入保留单&#xff0c;也有很大概率在趋势识别到转变之前就被减仓减掉了。 完整EA&#xff1a;Nerve Knife.ex4黄金交易策略_黄金趋势ea-CSDN博客 一、锁定单怎么样来的&#xf…

Git分支常用指令

目录 1 git branch 2 git branch xx 3 git checkout xx 4 git checkout -b xx 5 git branch -d xx 6 git branch -D xx 7 git merge xx(含快进模式和冲突解决的讲解) 注意git-log: 1 git branch 作用&#xff1a;查看分支 示例&#xff1a; 2 git branch xx 作用&a…

[day0] 借着“ai春晚”开个场

1 文思ai笔记-新的开始 今天是2024年2月29日&#xff0c;也是传统农历的除夕夜。早起在ai圈看到一个比较新奇的消息&#xff0c;ai春晚今日举办&#xff0c;竟然有一点小小的激动。这些年确实好久没看过春晚了&#xff0c;自己对于春晚的映像还停留在“白云黑土”、“今天&…

RocketMQ客户端实现多种功能

目录 RocketMQ客户端基本流程 消息确认机制 1、消息生产端采用消息确认加多次重试的机制保证消息正常发送到RocketMQ 单向发送 同步发送 异步发送 2、消息消费者端采用状态确认机制保证消费者一定能正常处理对应的消息 3、消费者也可以自行指定起始消费位点 广播消息 …

Java多线程:`Thread`类

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、Thread的常见构造方法二、Thread 的常见属性三、Thread的常用方法1、start方法2、中断一个线程Ⅰ、通过共享标记Ⅱ、调用in…

大模型基础架构的变革:剖析Transformer的挑战者(下)

上一篇文章中&#xff0c;我们介绍了UniRepLKNet、StripedHyena、PanGu-π等有可能会替代Transformer的模型架构&#xff0c;这一篇文章我们将要介绍另外三个有可能会替代Transformer的模型架构&#xff0c;它们分别是StreamingLLM、SeTformer、Lightning Attention-2&#xff…

蓝桥杯省赛模板构建——uart

打开CubeMX 串口的发送是跟调试器放一起的&#xff0c;通过PA9和PA10来接收发送 选择异步通讯 波特率配置为9600 打开串口中断&#xff0c;因为单片机接收数据需要用到中断 生成代码 添加底层驱动代码 打开在main.h打开uart定义 uart时钟配置&#xff0c;由于uart是用PCLK时钟…

STM32的ADC电压采集

时间记录&#xff1a;2024/2/9 一、ADC相关知识点 &#xff08;1&#xff09;STM32的ADC时钟不要超过14MHz&#xff0c;不然结果的准确率将下降 &#xff08;2&#xff09;ADC分为规则组和注入组&#xff0c;规则组相当于正常运行的程序&#xff0c;注入组相当于中断可以打断…

飞天使-linux操作的一些技巧与知识点8-zabbix6.0 容器搭建

文章目录 安装docker安装步骤mysql下载镜像安装zabbix 使用zabbix非host模式创建 测试效果 安装docker 1. 配置官方 yum 源$ sudo yum install -y yum-utils $ sudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo2. 安装 Docker$ …

CSGO游戏搬砖项目靠谱吗?是不是骗人的

很多地方都在大肆宣扬说CSGO游戏搬砖项目有二三十个点的利润&#xff0c;但我觉得他们看待问题太片面了&#xff0c;没有从全局上去分析这个项目。 这些人为了能割到小白的韭菜真是无所不用其极&#xff0c;什么牛都能吹得出来&#xff01;至少要实事求是吧&#xff0c;这不睁…

应用ANN+SMOTE+Keras Tuner算法进行信用卡交易欺诈侦测

目录 SMOTE&#xff1a; ANN&#xff1a;ANN(MLP) 三种预测-CSDN博客 Keras Tuner&#xff1a;CNN应用Keras Tuner寻找最佳Hidden Layers层数和神经元数量-CSDN博客 数据&#xff1a; 建模&#xff1a; SMOTE Sampling&#xff1a; Keras Tuner&#xff1a; SMOTE&…

【洛谷题解】B2056 求整数的和与均值

题目链接&#xff1a;求整数的和与均值 - 洛谷 题目难度&#xff1a;入门 涉及知识点&#xff1a;求和&#xff0c;平均值 题意&#xff1a; 输入样例&#xff1a; 4 344 222 343 222 输出样例&#xff1a; 1131 282.75000 分析&#xff1a;直接累加&#xff0c;再求平…

【Boost】:http_server模块(六)

http_server模块 一.安装cpp-httplib库二.基本使用服务器 一.安装cpp-httplib库 可以自己写一个http服务器&#xff0c;但比较麻烦&#xff0c;这里直接使用库。 在gitee上搜索cpp-httplib&#xff0c;任意找一个即可&#xff08;建议使用0.7.15版本&#xff09;。例如&#xf…

双重OSPF + OSPF综合实验

一、实验要求 1.R4为ISP&#xff0c;所连接的所有物理接口为公有网段&#xff0c;任意指定IP即可。 2.R1-2-3 构建一个星型结构的MGRE结构&#xff0c;其中R1为中心点&#xff0c;假设R1的公有IP为固定地址。 3.R1-5-6 构建另一个全连网状的MGRE网络&#xff0c;其中R1/5均为中…

STM32输出PWM波控制180°舵机

时间记录&#xff1a;2024/2/8 一、PWM介绍 &#xff08;1&#xff09;脉冲宽度调制 &#xff08;2&#xff09;占空比&#xff1a;高电平时间占整个周期时间的比例 &#xff08;3&#xff09;STM32通过定时器实现PWM时具有两种模式 PWM1模式&#xff1a;向上计数模式下&…

【错误收录】ohpm ERROR: Install failed FetchPackageInfo: @ohos/hypium failed

创建APP的时候出现这样一个错误&#xff0c;是代理没有配置的原因 ohpm.bat install --registry https://repo.harmonyos.com/ohpm/ ohpm WARN: ETIMEDOUT Failed to search for package "ohos/hypium" from "https://repo.harmonyos.com/ohpm/", request…

【初中生讲机器学习】6. 分类算法中常用的模型评价指标有哪些?here!

创建时间&#xff1a;2024-02-07 最后编辑时间&#xff1a;2024-02-09 作者&#xff1a;Geeker_LStar 你好呀~这里是 Geeker_LStar 的人工智能学习专栏&#xff0c;很高兴遇见你~ 我是 Geeker_LStar&#xff0c;一名初三学生&#xff0c;热爱计算机和数学&#xff0c;我们一起加…

【Java IO】同步异步和阻塞非阻塞真正的区别!!!

先上结论&#xff1a; 同步异步和阻塞非阻塞真正的区别&#xff01;&#xff01;&#xff01; 假设某个进程正在运行下面这段代码&#xff1a; ...... operatorA......; read(); operatorB......; operatorC......;当进程执行完operatorA后开始进行read系统调用&#xff0c;…