C++——二叉树

news2024/11/21 0:31:46

引入

 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.coloradmin.cn/o/1442340.html

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

相关文章

CSP-202109-1-数组推导

CSP-202109-1-数组推导 解题思路 如果 currentValue 与 previousValue 相同&#xff0c;说明这个值不是一个独特的新值&#xff0c;因此只将它加到 sumTotal 上。如果 currentValue 与 previousValue 不相同&#xff0c;说明这是一个新的独特值&#xff0c;因此既将它加到 su…

逐行拆解Guava限流器RateLimiter

逐行拆解Guava限流器RateLimiter 常见限流算法 计数器法 设置一个时间窗口内允许的最大请求量&#xff0c;如果当前窗口请求数超过这个设定数量&#xff0c;则拒绝该窗口内之后的请求。 关键词&#xff1a;时间窗口&#xff0c;计数器。 举个例子&#xff0c;我们设置1秒钟…

面试经典150题 -- 栈(总结)

总的链接 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 关于栈 -- stack 的学习链接 c的STL中的栈 -- stack-CSDN博客 20 . 有效的括号 这题直接用栈模拟就好了; 这里用一种取巧的方法 , 当遇见左括号&#xff0c;加入右…

【Langchain+Streamlit】旅游聊天机器人

【LangchainStreamlit】打造一个旅游问答AI-CSDN博客 项目线上地址&#xff0c;无需openai秘钥可直接体验&#xff1a;http://101.33.225.241:8502/ github地址&#xff1a;GitHub - jerry1900/langchain_chatbot: langchainstreamlit打造的一个有memory的旅游聊天机器人&…

Linux 命令基础

Shell概述 Linux操作系统的Shell作为操作系统的外壳&#xff0c;为用户提供使用操作系统的接口。它是命令语言、命令解释程序及程序设计语言的统称。 Shell是用户和Linux内核之间的接口程序&#xff0c;如果把硬件想象成一个球体的中心&#xff0c;内核围绕在硬件的外层管理着…

【JS逆向九】逆向某混淆网站源码,模拟 加密,解密,密钥生成

逆向日期&#xff1a;2024.02.09 使用工具&#xff1a;Node.js 是否有混淆&#xff1a;源代码混淆 加密方法&#xff1a;AES标准库 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 可使用AES进行解密处理&#xff08;直接解密即可&#xff0…

读千脑智能笔记09_大脑的错误信念

1. 人类智能的未来 1.1. 气候变化如此剧烈&#xff0c;在下一个百年里&#xff0c;一些城市很可能会不再适合人类居住&#xff0c;大面积的农业区或将变得更加贫瘠 1.1.1. 气候并非唯一需要关注的问题 1.1.2. 人类的一些技术&#xff0c;如核武器和基因编辑&#xff0c;为少…

win32编程系统BUG(Win32 API中的WM_SETTEXT消息)

由于频繁使用Win32 API中的WM_SETTEXT消息&#xff0c;导致内存占用直线上升。 暂未找到有效解决方案。

【SpringBoot】Redis集中管理Session和自定义用户参数解决登录状态及校验问题

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java对AI的调用开发》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、分布…

全网最快2024刘谦春晚魔术揭秘

早点关注我&#xff0c;精彩不错过&#xff01; 来来来&#xff0c;我的手机快被私信爆炸了&#xff0c;一次性给大家说清楚。 原版 Woody Arogon的教学《Woodyland》 数学原理 约瑟夫问题与魔术&#xff08;五&#xff09;——魔术《自我匹配的奇迹》中的数学原理 魔术原理 约…

vim常用命令以及配置文件

layout: article title: “vim文本编译器” vim文本编辑器 有三种模式: 命令模式 文本模式, 末行模式 vim命令大全 - 知乎 (zhihu.com) 命令模式 插入 i: 切换到输入模式&#xff0c;在光标当前位置开始输入文本。 a: 进入插入模式&#xff0c;在光标下一个位置开始输入文…

小巨人大爆发:紧凑型大型语言模型效率之谜揭晓!

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【数学建模】【2024年】【第40届】【MCM/ICM】【A题 七鳃鳗性别比与资源可用性】【解题思路】

我们通过将近半天的搜索数据&#xff0c;查到了美国五大湖中优势物种的食物网数据&#xff0c;以Eric伊利湖为例&#xff0c;共包含34各优势物种&#xff0c;相互之间的关系如下图所示&#xff1a; 一、题目 &#xff08;一&#xff09; 赛题原文 2024 MCM Problem A: Reso…

VBA技术资料MF117:测试显示器大小

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

数据结构(C语言)代码实现(八)——顺序栈实现数值转换行编辑程序括号分配汉诺塔

目录 参考资料 顺序栈的实现 头文件SqStack.h&#xff08;顺序栈函数声明&#xff09; 源文件SqStack.cpp&#xff08;顺序栈函数实现&#xff09; 顺序栈的三个应用 数值转换 行编辑程序 顺序栈的实现测试 栈与递归的实现&#xff08;以汉诺塔为例&#xff09; 参考资…

前端工程化面试题 | 02.精选前端工程化高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

秒杀相关问题解决

秒杀 超卖问题 如下,我们先来复现问题,抢购秒杀券的代码逻辑也是很简单, 先判断优惠券是否开始了,是的化,判断库存是否充足,如果是的化,扣减库存,最后创建订单 如下是代码 Override Transactional public Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVo…

备战蓝桥杯---动态规划之背包问题引入

先看一个背包问题的简单版&#xff1a; 如果我们暴力枚举可能会超时。 但我们想一想&#xff0c;我们其实不关心怎么放&#xff0c;我们关心的是放后剩下的体积。 用可行性描述即可。 于是我们令f[i][j]表示前i个物品能否放满体积为j的背包。 f[i][j]f[i-1][j]||f[i-1][j-v…

数据结构-->线性表-->单链表

链表的定义 链表&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 与顺序表不同的是&#xff0c;链表里的每节都是独立申请下来的空间&#xff0c;我们称之为“节点、结点”。 节点的组成主要由…

Electron+Vue实现仿网易云音乐实战

前言 这个项目是我跟着官方文档的那个Electron入门教程大致跑了一遍,了解了下Electron开发流程之后的实战项目,所以中间应该是会有很多写法不是很规范,安全性有可能也没考虑到,可实现的各种api也不是很了解,适合初学者。 必须感谢 https://github.com/Binaryify/NeteaseC…