C++ BinarySercahTree for version

news2024/9/21 4:20:06

搜索二叉树定义

 搜索二叉树模拟实现

首先写一个模版,然后写一个搜索二叉树的类 BSTree,类里面给 BSTe进行重命名为:Node。

template<class K>
class BSTree
{
	tyepdef BSTree<K> Node;
private:
	Node* root = nullptr;
};

再写一个搜二叉的结构体

template<class K>
struct BSTreeNode
{
	BSTreeNode< K>* _left;
	BSTreeNode< K>* _right;
	K _key;
};

 初始化列表:

struct BSTreeNode
{
	BSTreeNode< K>* _left;
	BSTreeNode< K>* _right;
	K _key;
	BSTree
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{

	}
};

 插入

如果根节点为空,就开辟一个节点,然后把要插入的值给这个节点:

bool Insert(const k& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
}

 如果跟节点不为空,就按搜索二叉树的性质来插入


即:如果key值比root值大,就把key值插在root节点右边,否则就插在root节点左边。

假如下图这个搜二叉,我们要插入个key=16,该怎么插:

 插入的历程如下:

 code

else{
Node* cur = _root;
			if (cur->_key < key)//如果key大
			{
				cur = cur->_right; //往右走
			}
			else if (cur->_key > key) //如果key小

			{
				cur = cur->_left; //往左走
			}
}

还有一种情况,那就是如果插入值和cur相等呢?

搜二叉不允许节点值相等,所以这就是为什么Insert函数给bool类型的原因。

如果相等就return false;

	else
		{
			Node* cur = _root;
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)

			{
				cur = cur->_left;
			}
			else
			{
				return false;
			}

上面的只是走一次,走到哪里结束呢?还需要再加个限制条件,我们让cur走到空的时候就不走了

 当while(cur为空就不走了):

else{	
while (cur)
			{
				Node* cur = _root;
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)

				{
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
}

开始插入

在为空的地方开辟一个新节点,把要插入的值push进这个新节点

	else
		{
			while (cur)
			{
				Node* cur = _root;
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)

				{
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(key);
			return true;
		}

ps:

虽然把值push进新节点了,但是此时新节点与搜二叉是一个断层状态,我们还需要把它们链起来。

可以弄一个快慢指针,cur先走,parent再走,当cur走完了,parent正好是cur的父亲节点,再把他俩链起来。

Node* cur = _root;
		Node* parent = nullptr;
		else
		{
			while (cur)
			{
				parent = cur;
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)

				{
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(key);
			if (cur->key < parent->_key)cur = parent->left;
			else if (cur->key > parent->_key)cur = parent->_right;
			return true;
		}

遍历

写一个中序来遍历

	void Inorder(Node * root)
		{
			if (root == nullptr)return;
			Inorder(root->_left);
			cout << root->_key << " ";
			Inorder(root->_right);
		}cout << endl;

写完了我们现在要调用Inorder

main

BSTree<int> bt;
bt.Inorder();

递归调用需要传参,我们应该传个root给inorder()。但是我们在main函数里拿不到root,列如:

 

方法一,写一个getroot函数,返回root,我们调用getroot就行了:

	Node* getroot()
		{
			return root;
		}
main


	bt.Inorder(bt.getroot());

方法2:我们外层写一个递归INORDER(),里面嵌套我们的_INORDER(),以此实现递归:

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

	
		void Inorder()
		{
			_Inoreder(_root);

		}
main



bt.Inorder( );

查找

查找key值,如果key比cur大就往右找,否则往左找,如果找到节点为空都找不到,返回false。如果找到了,返回true

		bool Find(const K& key)
		{
			Node* cur = _root;
			if (_root == nullptr)return;
			if (cur->_key < key)
			{
				cur = cur->right;
			}
			else if (cur->_key > key)
			{
				cur = cur->left;
			}
			else
			{
				//找遍了就是没找到,return false;
				return false;
			}
			return true;//否则找到节点为空就是找到了,return true;
		}

删除

我们都知道叶子节点最好删除,只要置空,然后free掉就可以了,如下图:

假设我们要删除根节点呢?

根据之前写二叉树的经验,根节点不能直接删除,再把其他节点挪上来,否则会改变树的结构。

而根节点处理好了,删除其他节点都可以按一样的方法进行处理。

我们可以用替换法,就是找一个节点与要交换的节点进行交换,但是交换之后仍然要保证搜二叉地的特性。

比如我可以把值为8节点和值为7节点进行交换:也可以把值为8节点与值为10节点交换:

我们发现规律:与左子树最大的值的节点交换/与右子树最小的值的节点交换。

然后再置空,free:

注意点:先写出框架:

	bool Erase(const K&	key	 )
		{
			Node* parent = nullptr;  //初始化
			Node* cur = root;

			//要删除数如果大于root就往右走
			if (cur->_key < key) 
			{
				cur = cur->right;
			}

			//要删除数如果大于root就往左走
			else if (cur->_key > key)
			{
				cur = cur->left;
			}
			else
			{
				//走到空,相等了,相等就是找到了
				//找到了就准备删除
			}

			 
		}

然后来写删除的具体步骤:

我们把8删掉了,就断层了:

我们需要重新链起来,可以这样写:

key->right=root->right

但是这样不完善,因为万一被删除的节点左边还有节点呢?

所以还要写一个

key->left=root->right

code

Node* parent = nullptr;  //初始化
			Node* cur = root;

if (cur->left == nullptr)
				{
					if (cur = parent->left)
					{
						parent->left = cur->right;
					}
					else if(cur = parent->right)
					{
						parent->right = cur->right;
					}
				}
else if (cur->right == nullptr)
				{

					if (cur = parent->left)
					{
						parent->left = cur->left;
					}
					else if (cur = parent->right)
					{
						parent->right = cur->left;
					}
				}

诈一看,好像没什么大问题,但实际上有纰漏。代码漏了一种情况,那就是下图这种情况:

 cur=8=root=key,8就是我们要删除的节点,8没有parent。

所以还需要加个条件:

if (cur == parent->left)
{
	cur = cur->right;
}

 

	if (cur = parent->left)
					{
						parent->left = cur->left;
					}

 还有一种情况,就是左右子树都不为空,例如3节点:

现在我们要删除3节点怎么删:

还是上面的方法,要么找右子树的最左节点(也就是右子树最小节点)或者找左子树最由节点(也就是左子树最大节点),然后进行替换。

拿找右子树的最左节点举例:
 

先向右走

Node* subleft = cur->_right;

 

 然后再判断右子树的左分支子树是否为空,不为空就往左走,走到空为止:

	else
				{
	Node* parent = cur;
					Node* subleft = cur->_right;
					while (subleft->left)
					{
						parent = subleft;
						subleft = subleft->_left;
					}
}

找到节点之后进行交换:

 

	swap(cur->_key, subLeft->_key);

					if (subLeft == parent->_left)
						parent->_left = subLeft->_right;
					else
						parent->_right = subLeft->_right;
				}

然后return true显示找到了值并且删除了:
 

return true;

整个删除就结束了,如果没有找到并且删除就return fales;

return false;

Erase完整code





	bool Erase(const K& key)
	{
		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
			{
				// 准备删除  20:15继续
				if (cur->_left == nullptr)
				{//左为空
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
				}
				else if (cur->_right == nullptr)
				{//右为空
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
				}
				else
				{//左右都不为空

					// 右树的最小节点(最左节点)
					Node* parent = cur;
					Node* subLeft = cur->_right;
					while (subLeft->_left)
					{
						parent = subLeft;
						subLeft = subLeft->_left;
					}

					swap(cur->_key, subLeft->_key);

					if (subLeft == parent->_left)
						parent->_left = subLeft->_right;
					else
						parent->_right = subLeft->_right;
				}

				return true;
			}
		}

		return false;
	}

 删除一下看看:

    bt.Inorder();
	printf("\n");
	
	bt.Erase(3);
	bt.Inorder( );
	printf("\n");


	bt.Erase(14);
	bt.Inorder();
	printf("\n");

 

改的话目前不能改,如我们把3改为80,那就改变搜二叉的结构了:

托管代码

BinarySearchTree for version ·  - 开源中国 (gitee.com)

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

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

相关文章

Qt中的枚举变量,Q_ENUM,Q_FLAG以及Qt中自定义结构体、枚举型做信号参数传递

Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他 理论基础&#xff1a;一、Q_ENUM二、QMetaEnum三、Q_FLAG四、示例 Chapter1 Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他前言Q_ENUM的使用Q_FLAG的引入解决什么问题&#xf…

怎么在Python爬虫中使用IP代理以避免反爬虫机制?

在进行网络爬虫的过程中&#xff0c;尤其是在大规模批量抓取数据时&#xff0c;需要应对各种反爬虫技术&#xff0c;其中最常用的就是IP封锁。为了避免IP被封锁&#xff0c;我们可以使用IP代理来隐藏自己的真实IP地址&#xff0c;从而让爬虫活动看起来更像正常的浏览器行为。 I…

Table-GPT:让大语言模型理解表格数据

llm对文本指令非常有用&#xff0c;但是如果我们尝试向模型提供某种文本格式的表格数据和该表格上的问题&#xff0c;LLM更有可能产生不准确的响应。 在这篇文章中&#xff0c;我们将介绍微软发表的一篇研究论文&#xff0c;“Table-GPT: Table- tuning GPT for Diverse Table…

虹科 | 解决方案 | 汽车示波器 远程诊断方案

车厂总部专家实时指导你修车 当一线汽修技师遇到疑难问题无从下手时&#xff0c;可以准备好pico汽车示波器套装&#xff0c;并戴上我们的M400智能AR眼镜&#xff0c;通过语音操作&#xff0c;呼叫主机厂的技术支持老师&#xff1b;老师通过AR眼镜上的摄像头老师可以实时看到现…

Python数据挖掘:入门、进阶与实用案例分析——基于非侵入式负荷检测与分解的电力数据挖掘

文章目录 摘要01 案例背景02 分析目标03 分析过程04 数据准备05 属性构造06 模型训练07 性能度量08 推荐阅读赠书活动 摘要 本案例将根据已收集到的电力数据&#xff0c;深度挖掘各电力设备的电流、电压和功率等情况&#xff0c;分析各电力设备的实际用电量&#xff0c;进而为电…

财务RPA机器人真的能提高效率吗?

财务部门作为一个公司的管理职能部门承担着一个公司在商业活动中各个方面的重要职责。理论上来说&#xff0c;一个公司的财务部门的实际工作包含但不限于对企业的盈亏情况进行评估、对风险进行预测、通过数据分析把握好公司的财务状况、税务管理等。 然而&#xff0c;实际上在…

分析主题帆软决策报表控件实现点击查询按钮后才能查询

点击「我的分析」即进入分析主题的管理界面&#xff0c;如下图所示&#xff1a; 2.1.1 分析列表区域 「分析列表」存放着当前用户创建的和其他用户协作给该用户的所有分析主题。 可以对分析列表和分析主题进行管理&#xff0c;详情参见&#xff1a;管理我的分析、管理分析主…

oa系统是什么?有哪些功能和作用?

本文将为大家讲解&#xff1a;1、oa系统是什么&#xff1f;有哪些功能和作用&#xff1f; 一、什么是OA系统 OA系统全称为Office Automation&#xff0c;即办公自动化系统。它是一种专门为企业和机构的日常办公工作提供服务的综合性软件平台&#xff0c;具有信息管理、流程管…

微信小程序实现文章内容详情

方案一、使用微信小程序官方提供的webview 前提已经在微信公众平台开发管理配置好了安全域名即&#xff1a; 方案二、把网页转成pdf直接展示 前提已经在微信公众平台开发管理配置好了安全域名即&#xff1a; 实现思路是发起网络请求拿到pdf下载地址&#xff0c;然后wx.download…

Linux (KDE) 中使用Network Settings设置静态ip

在 Linux (KDE) 中使用 Network Settings 设置s5静态IP详细教程 。 首先&#xff0c;打开 KDE 的设置面板。可以通过点击桌面上的设置图标&#xff0c;或者在开始菜单中搜索 “Settings” 并打开。 在设置面板中&#xff0c;点击 “Network” 选项。 接下来&#xff0c;你会看…

AD20生产Gerber文件

1、打开所要导出文件的PCB文件。并选择制造输出Gerber文件。 2、选择单位和格式 3、设置需要输出的图层 点击确定后&#xff0c;就可以生成Gerber文件。如下图所示。 4、生成钻孔文件 5&#xff0c;选择PCB窗口&#xff0c;跟第一步一样&#xff1b; 6&#xff0c;点击菜单 文…

讲座记录|1024学科周讲座分享

前言&#xff1a;2023年10月24日&#xff0c;我参加了学院举办的学科周活动&#xff0c;本篇文章记录一下参加本次活动的收获。 本次活动中&#xff0c;我主要参加了黄萱菁教授和高跃教授的讲座以及几位老师的Panel环节&#xff0c;然后聆听了网络空间安全分论坛的报告。在本次…

MySQL进阶(再论事务)——什么是事务 事务的隔离级别 结合MySQL案例详细分析

前言 MySQL最为最流行的开源数据库&#xff0c;其重要性不言而喻&#xff0c;也是大多数程序员接触的第一款数据库&#xff0c;深入认识和理解MySQL也比较重要。 本篇博客阐述MySQL的事务的定义和特性&#xff0c;原子性&#xff0c;一致性&#xff0c;隔离性&#xff0c;持久…

在 Windows 用 Chrome System Settings 设置代理

在 Windows 用 Chrome System Settings 设置代理 贴心提示&#xff1a;在设置代理之前&#xff0c;请确保您已经安装了 浏览器。 &#x1f527; 设置代理的详细步骤如下&#xff1a; 打开 浏览器&#xff0c;输入 //settings/system 并回车。 在「系统和网络设置」页面中&am…

突然发现柚子租车v1.42的小程序后端代码竟然内核文件全加密了!记录我的解密过程

因客户需求&#xff0c;需要定制租车小程序源码&#xff0c;找了很多要么很贵要么功能不满足需求&#xff0c;为啥这么折腾&#xff1f;还不是因为客户给的资金有限&#xff01; 翻来覆去找到了柚子租车这个小程序功能各方面都能满足需求&#xff0c;然后我就尝试找最新版&…

Find My眼镜|苹果Find My技术与眼镜结合,智能防丢,全球定位

近年来&#xff0c;得益于国家政策的大力支持和技术的不断进步&#xff0c;眼镜产品的发展取得了长足的进步&#xff0c;正朝着高质量、多元化方向迈进。在这个趋势下&#xff0c;眼镜的功能性、时尚性、舒适性等方面都有了明显的提升&#xff0c;同时为了满足不同年龄段消费者…

[黑马程序员SpringBoot2]——基础篇2

目录&#xff1a; 模块创建实体类快速开发(lombok)数据层标准开发&#xff08;基础CRUD)开启MP运行日志分页数据层标准开发(条件查询)业务层标准开发&#xff08;基础CRUD)业务层标准开发&#xff08;基于MyBatisPlus构建)表现层标准开发表现层数据一致性处理&#xff08;R对象…

阿里云企业邮箱基于Spring Boot快速实现发送邮件功能

邮件在项目中经常会被用到&#xff0c;比如用邮件发送通知。比如&#xff0c;通过邮件注册、认证、找回密码、系统报警通知、报表信息等。本篇文章带大家通过SpringBoot快速实现一个发送邮件的功能。 邮件协议 下面先简单了解一下常见的邮件协议。常用的电子邮件协议有SMTP、…

xxl-job项目集成实战,全自动项目集成,可以直接使用到项目中

如果你看官方文档&#xff0c;在研究透&#xff0c;至少也得几天时间&#xff0c;如果你直接看我的文档&#xff0c;快速用到项目中&#xff0c;也就10分钟就搞好了。 xxl-job功能确实很强大&#xff0c;而且使用的人比较多&#xff0c;既然在使用xxl-job&#xff0c;那肯定是…

成为CSS选择器大师,让你的网页瞬间提升品味!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、选…