【C++】二叉搜索树(手撕插入、删除、寻找)

一、什么是二叉搜索树

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

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

二、二叉搜索树的操作

2.1二叉搜索树的寻找

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

b、最多查找高度次,走到到空,还没找到,这个值不存在。

bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (key > cur->_key)
		{
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			cur = cur->_left;
		}
		else
		{
			return true;
		}
		return false;
	}
}
2.2二叉搜索树的插入

插入的具体过程如下:

a. 树为空,则直接新增节点,赋值给root指针

b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	Node* curparent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (key > cur->_key)
		{
			curparent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			curparent = cur;
			cur = cur->_left;
		}
		else
		{
			//找到相同元素就报错
			return false;
		}
	}
	cur = new Node(key);
	if (cur->_key > curparent->_key)
	{
		curparent->_right = cur;
	}
	else
	{
		curparent->_left = cur;
	}
	return true;
}
2.3二叉搜索树的删除

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

  • 情况1:要删除的结点无孩子结点。

让父亲节点指向孩子节点的左节点或右节点即可(指向nullptr),该种情况可以在情况2和情况3处理

        

  • 情况2:要删除的结点只有左孩子结点。

如果删除节点是左孩子,那就让父亲节点的左指针指向删除节点的左节点,如果删除节点右孩子,那就让父亲节点的右指针指向删除节点的左节点(还要注意父亲节点不存在,及删除的是根节点的情况)

  • 情况3:要删除的结点只有右孩子结点。

如果删除节点是左孩子,那就让父亲节点的左指针指向删除节点的右节点,如果删除节点右孩子,那就让父亲节点的右指针指向删除节点的右节点

  • 况4:要删除的结点有左、右孩子结点。

找左子树的最大节点或者右子树的最小节点与删除节点的值互换(只有这两个节点满足二叉搜索树的性质),然后删除。以找右子树最小节点举例,交换值以后,让最小节点的父亲节点的左指针指向最小节点的右节点,因为右子树最小节点是最左边的节点,但他可能存在右孩子。

要注意特殊情况,右子树最小节点就是删除节点的右孩子,此时就要让父亲节点的右指针指向删除节点的右孩子

bool Erase(const K& key)
{
	Node* curparent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (key > cur->_key)
		{
			curparent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			curparent = cur;
			cur = cur->_left;
		}
		else
		{
			//删除操作
			//如果删除节点左子树为空
			if (cur->_left == nullptr)
			{
				if (_root == cur)
				{
					_root = _root->_right;
				}
				else
				{
					if (curparent->_left == cur)
					{
						curparent->_left = cur->_right;
					}
					else
					{
						curparent->_right = cur->_right;
					}
				}
				delete cur;
			}
			//如果删除节点右子树为空
			else if (cur->_right == nullptr)
			{
				if (_root == cur)
				{
					_root = _root->_left;
				}
				else
				{
					if (curparent->_left == cur)
					{
						curparent->_left = cur->_left;
					}
					else
					{
						curparent->_right = cur->_left;
					}
				}
				delete cur;
			}
			else
			{
				//删除节点左右都不为空
				Node* RightMinParent = cur;
				Node* RightMin = cur->_right;
				while (RightMin->_left)
				{
					RightMinParent = RightMin;
					RightMin = RightMin->_left;
				}
				swap(RightMin->_key, cur->_key);

				if (RightMinParent->_left == RightMin)
				{
					RightMinParent->_left = RightMin->_right;
				}
				else
				{
					RightMinParent->_right = RightMin->_right;
				}
				delete RightMin;
			}
			return true;
		}
	}
	return false;
}

三、二叉搜索树的应用

K模型K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

       比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

  • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

KV模型每一个关键码key,都有与之对应的值Value,即的键值对。该种方式在现实生活中非常常见:

  • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文就构成一种键值对;
  • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是就构成一种键值对。

ps、KV模型的二叉搜索树与K模型的二叉搜索树相类似,因为KV模型的删除、寻找等操作是依靠key的与value值无关

namespace key_value
{
	template<class K,class V>
	struct BSTreeNode
	{
		BSTreeNode(const K& key,const V& value)
			:_left(nullptr), _right(nullptr), _key(key),_value(value)
		{}

		struct BSTreeNode* _left;
		struct BSTreeNode* _right;
		K _key;
		V _value;
	};
	template<class K,class V>
	class BSTree
	{
		typedef struct BSTreeNode<K,V> Node;
	private:
		//销毁二叉搜索树
		void Destory(Node* root)
		{
			//后续递归删除
			if (root == nullptr)
			{
				return;
			}
			Destory(root->_left);
			Destory(root->_right);
			delete root;
		}
	public:
		~BSTree()
		{
			Destory(_root);
			_root = nullptr;
		}
		bool Insert(const K& key,const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key, value);
				return true;
			}
			Node* curparent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (key > cur->_key)
				{
					curparent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					curparent = cur;
					cur = cur->_left;
				}
				else
				{
					//找到相同元素就报错
					return false;
				}
			}
			cur = new Node(key,value);
			if (cur->_key > curparent->_key)
			{
				curparent->_right = cur;
			}
			else
			{
				curparent->_left = cur;
			}
			return true;
		}

		void _Inorder(Node* ret)
		{
			if (ret == nullptr)
				return;
			_Inorder(ret->_left);
			cout << ret->_key << ":"<<ret->_value<<endl;
			_Inorder(ret->_right);
		}

		void Inorder()
		{
			_Inorder(_root);
			cout << endl;
		}

		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
				return false;
			}
		}

		bool Erase(const K& key)
		{
			Node* curparent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (key > cur->_key)
				{
					curparent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					curparent = cur;
					cur = cur->_left;
				}
				else
				{
					//删除操作
					//如果删除节点左子树为空
					if (cur->_left == nullptr)
					{
						if (_root == cur)
						{
							_root = _root->_right;
						}
						else
						{
							if (curparent->_left == cur)
							{
								curparent->_left = cur->_right;
							}
							else
							{
								curparent->_right = cur->_right;
							}
						}
						delete cur;
					}
					//如果删除节点右子树为空
					else if (cur->_right == nullptr)
					{
						if (_root == cur)
						{
							_root = _root->_left;
						}
						else
						{
							if (curparent->_left == cur)
							{
								curparent->_left = cur->_left;
							}
							else
							{
								curparent->_right = cur->_left;
							}
						}
						delete cur;
					}
					else
					{
						//删除节点左右都不为空
						Node* RightMinParent = cur;
						Node* RightMin = cur->_right;
						while (RightMin->_left)
						{
							RightMinParent = RightMin;
							RightMin = RightMin->_left;
						}
						swap(RightMin->_key, cur->_key);

						if (RightMinParent->_left == RightMin)
						{
							RightMinParent->_left = RightMin->_right;
						}
						else
						{
							RightMinParent->_right = RightMin->_right;
						}
						delete RightMin;
					}
					return true;
				}
			}
			return false;
		}
	private:
		Node* _root = nullptr;
	};

	void test()
	{
		BSTree<string,string> t;
		t.Insert("apple", "苹果");
		t.Insert("pear", "梨");
		t.Insert("pen", "笔");
		t.Insert("insert", "插入");
		t.Erase("apple");
		t.Erase("pen");
		t.Inorder();
		t.~BSTree();
	}
}

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

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

相关文章

collections模块

tuple的功能 只有可哈希的对象才可以作为字典的key&#xff0c;而immutable的对象是可哈希的 tuple的拆包&#xff0c;分别进行映射 拆包的灵活用法 tuple的不可变不是绝对的 nametuple的详解 __slots__是用于限制class里面有那些属性值的&#xff0c;可以自行去了解一下 f…

Python基础详解三

一&#xff0c;函数的多返回值 def methodReturn():return 1,2x,ymethodReturn() print(x,y) 1 2 二&#xff0c;函数的多种参数使用形式 缺省参数&#xff1a; def method7(name,age,address"淄博"):print("name:"name",age"str(age)&quo…

你需要知道vim操作 源头都在vimtutor里

vim之源&#xff1a;vimtutor vim的五种模式Normal mode&#xff08;正常模式&#xff09;Insert mode&#xff08;插入模式&#xff09;Visual mode&#xff08;可视模式&#xff09;Replace mode&#xff08;替换模式&#xff09;Command-line mode&#xff08;命令行模式&am…

Gradle 基础学习(三) 认识Command-Line Interface

Gradle命令行接口 除了IDE外&#xff0c;我们主要通过Gradle命令行接口来运行Gradle任务和管理Gradle项目。 下面是Gradle命令行使用的一些参考&#xff0c;熟悉后建议实际项目中使用Gradle Wrapper&#xff0c;gradle用法都可以替换为gradlew (macOS / Linux) 或gradlew.bat…

【SDN:逻辑上集中的控制平面,路由选择算法,LS路由工作过程,距离矢量路由选择(distance vector routing)】

文章目录 SDN&#xff1a;逻辑上集中的控制平面SDN的主要思路SDN控制平面和数据平面分离的优势SDN 架构: 数据平面交换机 路由选择算法路由(route)的概念最优化原则(optimality principle)路由的原则路由算法的分类LS路由工作过程&#xff08;相当于一个上帝&#xff09;链路状…

保护公司机密:避免员工带着数据说拜拜

公司的核心资产之一就是数据。无论是客户信息、研发代码、内部决议、财务报告、商业合同、设计图纸等都是公司的重要资产。如果这些数据在员工离职时被带走&#xff0c;或在员工在职期间不当行为导致数据泄露&#xff0c;将给公司带来重大损失。 然而&#xff0c;保护这些数据…

Ps中 饱和度 和 自然饱和度 的区别?

1.饱和度&#xff08;Saturation&#xff09;&#xff1a;在Photoshop中&#xff0c;饱和度是一个全局性调整&#xff0c;它影响图像中所有颜色的鲜艳程度。当你增加饱和度时&#xff0c;所有的颜色都会变得更浓烈、更鲜艳&#xff1b;相反&#xff0c;减小饱和度会使图像整体变…

暗区突围国际服pc端海外版新手前期如何赚钱 暗区突围新手教学

暗区突围国际服pc端海外版新手前期如何赚钱 暗区突围新手教学 暗区突围是一款极为惊险的射击游戏&#xff0c;让玩家充分感受紧张激烈的战斗以及获取财富的过程。但是有许多新手玩家是不会在游戏里赚钱的&#xff0c;也会在赚钱过程中遇到很多问题&#xff0c;我将在这篇文章…

Learning Continuous Image Representation with Local Implicit Image Function

CVPR2021https://github.com/yinboc/liif 问题引入 图像普遍都是使用像素来表示的&#xff0c;而现实世界是连续的&#xff0c;所以本文借鉴3D中neural implicit representation的思想&#xff0c;以连续的方式表示图像&#xff1b;模型输入坐标值和坐标附近的特征&#xff0…

区块链 | NFT 水印:Review on Watermarking Techniques(一)

&#x1f34d;原文&#xff1a;Review on Watermarking Techniques Aiming Authentication of Digital Image Artistic Works Minted as NFTs into Blockchains 1 应用于 NFT 的水印技术 常见的水印技术类型可以分为&#xff1a; 可见 v i s i b l e \mathsf{visible} visi…

关于Anaconda常用的命令

常用命令 查看当前环境下的环境&#xff1a;conda env list查看当前conda的版本&#xff1b;conda --version conda create -n your_env_name pythonX.X&#xff08;2.7、3.6等)命令创建python版本为X.X。名字为your_env_name的虚拟环境。your_env_name文件可以在Anaconda安装…

2024第16届成都教育连锁加盟展6月1日举办 免费参观

2024第16届成都教育连锁加盟展6月1日举办 免费参观 邀请函 主办单位&#xff1a; 中国西部教体融合博览会组委会 承办单位&#xff1a;重庆港华展览有限公司 博览会主题&#xff1a;责任教育科技兴邦 幼教、普教、高教、校外教育、K12学科辅导、婴幼儿教育、兴趣辅导、学…

STC8增强型单片机开发

1.C51版本Keil环境搭建 下载地址是 Keil Product Downloads 选择C51进行下载&#xff1a; 2.STC环境添加 STC-ISP下载 进入stc官网 深圳国芯人工智能有限公司-工具软件 3.将STC添加到Keil中 打开stc-isp工具 按照图例点击按钮 选择keil的安装目录&#xff0c;以实际安装目…

Nacos单机模式集成MySQL

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Nacos支持三种部署…

VALSE 2024 Workshop报告分享┆ 大规模自动驾驶仿真系统研究

视觉与学习青年学者研讨会&#xff08;VALSE&#xff09;旨在为从事计算机视觉、图像处理、模式识别与机器学习研究的中国青年学者提供一个广泛而深入的学术交流平台。该平台旨在促进国内青年学者的思想交流和学术合作&#xff0c;以期在相关领域做出显著的学术贡献&#xff0c…

五月加仓比特币

作者&#xff1a;Arthur Hayes Co-Founder of 100x. 编译&#xff1a;Liam 编者注&#xff1a;本文略有删减 (以下内容仅代表作者个人观点&#xff0c;不应作为投资决策的依据&#xff0c;也不应被视为参与投资交易的建议或意见&#xff09;。 从四月中旬到现在&#xff0c;当你…

动态规划——路径问题:931.下降路径最小和

文章目录 题目描述算法原理1.状态表示&#xff08;经验题目&#xff09;2.状态转移方程3.初始化4.填表顺序5.返回值 代码实现CJava 题目描述 题目链接&#xff1a;931.下降路径最小和 关于这⼀类题&#xff0c;看过我之前的博客的朋友对于状态表示以及状态转移是⽐较容易分析…

Java 中的 HTTP 客户端库OkHttp、Apache HttpClient和HttpUrlConnection

大家好&#xff0c;我是G探险者。 项目开发里面经常会有这么一种场景&#xff1a;与服务器进行 HTTP 通信。一般存在于服务间远程调用的场景 Java 生态系统提供了多种 HTTP 客户端库&#xff0c;每种都有其自己的特点、优势和适用场景。 本文将介绍几种主要的 Java HTTP 客户…

【练习3】

1.将二叉搜索树转为排序的双向链表 (好久没看数据结构&#xff0c;忘完了&#xff0c;学习大佬的代码&#xff09; class Solution { public:Node* prenullptr,*headnullptr; //pre为每次遍历时的前一个节点&#xff0c;head记录头节点Node* treeToDoublyList(Node* root) {if…

Qt应用开发(拓展篇)——图表 QChart

一、前言 QChart是一个图形库模块&#xff0c;它可以实现不同类型的序列和其他图表相关对象(如图例和轴)的图形表示。要在布局中简单地显示图表&#xff0c;可以使用QChartView来代替QChart。此外&#xff0c;线条、样条、面积和散点序列可以通过使用QPolarChart类表示为极坐标…
最新文章