搜索树的逻辑以及代码实现-重点删除结点的逻辑

news2024/11/26 20:28:45

 

目录

搜索二叉树概念

代码

 二叉搜索树的插入

二叉搜索树的查找

二叉搜索树的删除(非常重要)

a、b、c情况:删除14

d情况

打印结点中序遍历

 整体代码:(有R开头的就是递归写法,逻辑一样)


搜索二叉树概念

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

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

错误例子:

1、不是搜索树,数据为0不应该在这个位置,应为它比他的左子树还要小

2、不是搜索树,数据9虽然比自己的父亲6大位置合适,但是他的祖父8的左子树

3、不是搜索树,数据10虽然比自己的父亲14小位置合适,但是祖父10相等,但是搜索树里面的key值必须唯一值。 


代码

 二叉搜索树的插入

二叉树的链接一定在叶子结点上,现在我们要插入7这个数据,从头开始比较寻找数据,使用2个指针,一个指针保存寻找路径。一个指针(cur)保存寻找路径,一个保存(prev)父节点。

8>5  ,prev=cur,cur=cur->left

3<5,prev=cur,cur=cur->right

 6>5,prev=cur,cur=cur->left

4<5,prev=cur,cur=cur->right

 当cur到空,开始链接新结点,new一个node结点,然后cur指向新结点。

 让prev的空子树指向cur。这里有个俩问题我们并不知道链接到prev的左子树还是右子树,如果用空子树判断,若是左右子树都为空,就无法判断

所以我们唯一的方式就是比较prev与cur现在保存的key。

perv->key<cur->key;perv->right=cur;

perv->key>cur->key;perv->left=cur;

 因为上一层的循环查找也这个比较条件到了空子树,所以再一次比较数据就知道应该链接的子树了。

二叉搜索树的查找

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

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

二叉搜索树的删除(非常重要)

a. 要删除的结点无孩子结点

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

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

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

无孩子和只有一个孩子可以合并,应为删除后我们可以父节点指向该结点的空子树的另一端。

a、b、c情况:删除14

情况一:条件->cur在prev右子树

        当我们删除14时候,需要判断14的左右子树哪个为空(都为空也行)。应为我们需要将cur的非空子树链接(也可以是空)。

 cur结点左子树为空,prev->right指向cur->right

cur结点右子树为空,prev->left指向cur->left

cur结点左子树为空,prev->right直接指向cur->right,不论是否为空(解决a情况)。

但是我们的前提有个条件:cur在prev右子树,所以我们还得判断一下cur在prev的哪一边。

 我们的寻找结点可是左右左右的寻找。

 但是还有一种情况还没判断,就是cur==root(根节点)。

 

 

 可以在cur判断左右空子树的if-else里做if判断,如果为cur==root让root=cur->非空子树(也可以空)。

//判断左右子树是否为空,只要有一个为空就会进入第一种删除:普通删除结点
//+++++++++++++++++++++++++++++第一种开始++++++++++++++++++++++++++++++++
//cur的左树为空
if (cur->_left == nullptr)
{
//判断是否为根结点
    if (cur == _root)
	{
		_root = cur->_right;
	}
	else
	{
        //判断是在cur在prev左右那一颗子树
	    if (prev->_left == cur)
	    {
		    prev->_left = cur->_right;
		}
		else
		{
			prev->_right = cur->_right;
		}
	}
	delete cur;
	cur = nullptr;
}
//cur的左树为空
else if (cur->_right == nullptr)
{
    //......
}
//+++++++++++++++++++++++++++++第一种结束++++++++++++++++++++++++++++++++

d情况

当我们要删除一个拥有双非空子树结点时,就不可以普通删除了,极可能破坏搜索树结构

单纯删除3结点,我们的父节点根本无法判断如何链接被这被删除的结点下的子树,链接6就要放弃1,链接1就要放弃6.这是我们不愿意看到的结果。所以当被删除的结点左右树都为非空时,我们需要换一个方法删除3结点--->替换法

 我们来删除3结点来演示,我们寻找3结点右数的极小值做替换删除演示。

首先寻找cur删除结点右子树蓝圈中的最小值,定义2个结点寻找极值,一个结点保存极值结点,一个保存极值的父节点。初始值:fdele=cur,dele=cur->right

 

 

循环寻找代码为:

dele=cur->right;
fdele=cur;
whlie(dele->left!=nullptr)
{
    fdele=dele;
    dele=dele->left;
}

一旦循环结束就意味着到了cur右子树的最左结点来到了极值。

找到了,直接将dele->key赋值给cur->key

cur->key=dele->key;

然后开始删除dele。

情况一,fdele!=cur;

情况二:fdele==cur/dele=cur->left`;意味着并没有进入循环

链接后然后再删除dele

替换删除代码:

//判断左右子树是否为空,只要有一个为空就会进入第一种删除:普通删除结点
//+++++++++++++++++++++++++++++第一种开始++++++++++++++++++++++++++++++++
//cur的左树为空
if (cur->_left == nullptr)
{
    //......
}
//cur的左树为空
else if (cur->_right == nullptr)
{
    //......
}
//+++++++++++++++++++++++++++++第一种结束++++++++++++++++++++++++++++++++
//=============================替换删除==================================
else
{
	//---替换法删除
	Node* dele = cur->_right;
	Node* fdele = cur;
	while (dele->_left)
	{
		fdele = _cur;
		dele = _cur->_left;
	}
	cur->_key = dele->_key;
	//也解决了cur==root
	if (fdele == cur)
    //if(cur->right==dele)
	{
		fdele->_right = dele->_right;
	}
	else
	{
		fdele->_left = dele->_right;
	}
	delete dele;
}

打印结点中序遍历

我们用中序遍历会发现出来的数据为排序好的。

 整体代码:(有R开头的就是递归写法,逻辑一样)

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

		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
			}
			else
			{

				Node* cur = _root;
				Node* prev = cur;
				while (cur != nullptr)
				{
					prev = cur;
					if (cur->_key < key)
					{
						cur = cur->_right;
					}
					else if (cur->_key > key)
					{
						cur = cur->_left;
					}
					else
					{
						return false;
					}
				}
				cur = new Node(key);
				if (prev->_key > cur->_key)
				{
					prev->_left = cur;
				}
				else
				{
					prev->_right = cur;
				}
			}
		}

		void Inorder()
		{
			_Inorder(_root);
		}

		bool RInsert(const K& key)
		{
			//if (!_root)
			//{
			//	_root = new Node(key);
			//	return true;
			//}
			return _RInsert(_root, key);
		}

		Node* RFind(const K& key)
		{
			return _RFind(_root, key);
		}

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

		Node* Erase(const K& key)
		{
			Node* cur = _root;
			Node* prev = cur;
			while (cur != nullptr)
			{

				if (cur->_key < key)
				{
					prev = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					prev = cur;
					cur = cur->_left;
				}
				else
				{
					//普通删除delete-->cur
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (prev->_left == cur)
							{
								prev->_left = cur->_right;
							}
							else
							{
								prev->_right = cur->_right;
							}
						}
						delete cur;
						cur = nullptr;
					}
					//普通删除delete-->cur
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (prev->_left == cur)
							{
								prev->_left = cur->_left;
							}
							else
							{
								prev->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else
					{
						//---替换法删除
						Node* _cur = cur->_right;
						Node* _prev = cur;
						while (_cur->_left)
						{
							_prev = _cur;
							_cur = _cur->_left;
						}
						cur->_key = _cur->_key;
						//也解决了cur==root
						if (_prev == cur)
						{
							_prev->_right = _cur->_right;
						}
						else
						{
							_prev->_left = _cur->_right;
						}
						//if (_prev->_left == _cur)
						//{
						//	_prev->_left = cur->_right;
						//}
						//else
						//{
						//	_prev->_right = cur->_right;
						//}
						delete _cur;
					}
					return cur;
				}
			}
			return nullptr;
		}
		bool RErase(const K& key)
		{
			return	_RErase(_root, key);
		}
		BSTree() = default;

		BSTree<K>& operator=(BSTree<K> t)
		{
			std::swap(_root, t._root);
			return *this;
		} 

		~BSTree()
		{
			_Destroy(_root);
		}

		BSTree(BSTree<K>&t)
		{
			_root = _Copy(t._root);
		}

	private:

		Node* _Copy(Node*&root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* copyRoot = new Node(root->_key);
			copyRoot->_left = _Copy(root->_left);
			copyRoot->_right = _Copy(root->_right);
			return copyRoot;
		}

		void _Destroy(Node*&root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Destroy(root->_left);
			_Destroy(root->_right);
			delete root;
			root = nullptr;
		}

		bool _RErase(Node*& root, const K&key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (key < root->_key)
			{
				return _RErase(root->_left, key);
			}
			else if(key>root->_key)
			{
				return _RErase(root->_right, key);
			}
			else
			{
				Node* del = root;
				//普通删除
				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				//普通删除
				else if(root->_right == nullptr)
				{
					root = root->_left;
				}
				//替换+递归删除
				else
				{
					Node* _cur = root->_right;
					Node* _prev = root;
					while (_cur->_left)
					{
						_prev = _cur;
						_cur = _cur->_left;
					}
					/*K tmp = _cur->_key;
					RErase(tmp);
					root->_key = tmp;*/
					std::swap(root->_key, _cur->_key);
					return _RErase(root->_right, key);
					//if (_prev == root)
					//{
					//	_prev->_right = _cur->_right;
					//}
					//else
					//{
					//	_prev->_left = _cur->_right;
					//}
				}
				delete del;
				return true;
			}
		}

		//bool _RInsert(Node* root, const K& key)
		//{
		//	if (root->_left == nullptr && root->_key > key)
		//	{
		//		root->_left = new Node(key);
		//		return true;
		//	}
		//	if (root->_right == nullptr && root->_key < key)
		//	{
		//		root->_right = new Node(key);
		//		return true;
		//	}
		//	if (root->_key < key)
		//	{
		//		return _RInsert(root->_right, key);
		//	}
		//	else if (root->_key > key)
		//	{
		//		return _RInsert(root->_left, key);
		//	}
		//	else
		//	{
		//		return false;
		//	}
		//}
		bool _RInsert(Node*& root, const K& key)
		{
			//神之一笔:引用上级子树的引用,这里先当与
			//(上一级)root->left(right)=new Node(key)
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			if (key > root->_key)
			{
				return _RInsert(root->_right, key);
			}
			else if(key<root->_key)
			{
				return _RInsert(root->_left, key);
			}
			else
			{
				return false;
			}
		}

	


		Node* _RFind(Node*root,const K& key)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			if (root->_key < key)
			{
				return _RFind(root->_right,key);
			}
			else if (root->_key > key)
			{
				return _RFind(root->_left, key);
			}
			else
			{
				return root;
			}
		}


		void _Inorder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Inorder(root->_left);
			std::cout << root->_key << ' ';
			_Inorder(root->_right);
		}
		Node* _root = nullptr;
	};

 

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

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

相关文章

合宙Air724UG Cat.1模块硬件设计指南--UART接口

UART接口 简介 串行接口简称串口&#xff0c;同时串口又分为同步收发串口与异步收发串口两种方式&#xff0c;而CAT1模块支持的串口为异步收发方式&#xff0c;简称UART口。串口作为设备的外部接口&#xff0c;是调试开发的一种重要的开发手段。 特性 UART1串口 包括数据线TXD…

ubuntu (Linux mint) 安装mysql 5.7

1 从官网下载deb格式安装包 网址&#xff1a; https://downloads.mysql.com/archives/community/ 2 解压安装包 执行命令&#xff1a; tar -xvf mysql-server_5.7.41-1ubuntu18.04_amd64.deb-bundle.tar 如下图所示&#xff1a; 你会发现多了很多.deb文件。 3 批量安装 .d…

当C/C++遇上高性能计算|会C/C++编程的看过来,这类岗位是真香

C/C编程语言作为经典而强大的编程语言&#xff0c;一直在计算机科学领域占据着重要的地位。而当C/C遇上高性能计算&#xff0c;这个组合将带给你无尽的机遇和挑战&#xff0c;成为一名高性能计算工程师&#xff0c;你会发现这类岗位是真香。从此告别爹不疼、娘不爱的尴尬境地。…

【RH850/U2A】:Task激活过程

Task激活过程 Autostart非AutostartTASK(Default_Init_Task)EcuM_StartupTwo(void)SchM_Init(void)BswM_Init(xx)Rte_Start(void)Task激活过程,分自动运行(AutoStart)和非自动运行。 Autostart 在DavinciCfg中的配置如下: Autostart意味作在执行StartOS()后就开始运行了。…

使用Python进行小波变化

上图展示了使用Python小波变换的一个使用案例&#xff0c;数据和代码来自https://github.com/ct6502/wavelets。在使用运行代码的时候发现了源代码的报错分别在Line115和Line136&#xff0c;是将Y轴变为对数轴&#xff0c;需要将“plt3.set_yscale(‘log’,base2, subsNone)”改…

pycharm HfArgumentParser ArgumentParser 参数设置 以及Debug

HfArgumentParser可以将类对象中的实例属性转换成转换为解析参数。必须注意的是&#xff0c;这里的类对象必须是通过dataclass()创建的类对象。并且通过HfArgumentParser创建的解析参数&#xff0c;都是可选参数。 from transformers import HfArgumentParser from dataclasse…

Pytest教程__fixture(9)

什么是fixture fixture是pytest特有的功能&#xff0c;使用装饰器 pytest.fixture 标记的函数在其他函数中能被当作参数传入并被调用。 fixture有明确的名字&#xff0c;在其他函数&#xff0c;模块&#xff0c;类或整个工程调用它时会被激活。 fixture是基于模块来执行的&a…

【计算机视觉 | 分割】SAM 升级版:HQ-SAM 的源代码测试(含测试用例)

文章目录 一、第一段代码二、第二段代码三、第三段代码3.1 函数13.2 函数23.3 函数33.4 函数43.5 函数5 四、第四段代码五、第五段代码5.1 测试用例15.2 测试用例25.3 测试用例35.4 测试用例45.5 测试用例55.6 测试用例65.7 测试用例75.8 测试用例8 下面是一个测试用例&#xf…

代理模式(十五)

相信自己&#xff0c;请一定要相信自己 上一章简单介绍了享元模式(十四), 如果没有看过, 请观看上一章 一. 代理模式 引用 菜鸟教程里面的代理模式介绍: https://www.runoob.com/design-pattern/proxy-pattern.html 在代理模式&#xff08;Proxy Pattern&#xff09;中&…

阿里云服务器租用费用_2023价格表

2023年阿里云服务器租用费用&#xff0c;阿里云轻量应用服务器2核2G3M带宽轻量服务器一年108元&#xff0c;2核4G4M带宽轻量服务器一年297.98元12个月&#xff0c;阿里云u1服务器2核4G、2核8G、4核8G、8核16G、4核16G、8核64等配置新人3折&#xff0c;云服务器c7、g7和r7均有活…

责任链模式:构建一条责任链去处理不同级别的日志信息

概要 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;在有多个对象&#xff08;处理者&#xff09;都可以接收请求的情况下&#xff0c;允许你将多个对象连接成一条处理链&#xff0c;请求沿着处理链进行发送。收到请求后…

最新水文水动力模型在城市内涝、城市排水、海绵城市规划设计中深度应用丨SWMM排水管网水力、水质建模及海绵与水环境应用

目录 第一部分 CAD、GIS在水力建模过程中的应用 第二部分 SWMM模型深度应用 第三部分 城市内涝一维二维耦合模拟 第四部分 海绵城市关键控制指标计算 第五部分 SWMM二次开发基础 SWMM排水管网水力、水质建模及在海绵与水环境中的应用 随着计算机的广泛应用和各类模型软件…

基于AutoJs7实现的薅羊毛App专业版源码大分享

源码下载链接&#xff1a;https://pan.baidu.com/s/1QvalXeUBE3dADfpVwzF_xg?pwd0736 提取码&#xff1a;0736 专业版肯定比个人版功能强大并且要稳定。增加了很多功能的同时也测试封号的App&#xff0c;对于封号的App&#xff0c;给予剔除。虽然App数量减少了但是都是稳定的…

getopt函数和getopt_long函数

这个函数有点像无限迷宫&#xff0c;正确的路和错误的路都有很多&#xff0c;我们只需要能够满足当前需求就可以了&#xff0c;完全没有必要去探索每一条路。虽然&#xff0c;我很久以前试图这样干过。过滤后的回忆&#xff0c;只剩感觉了&#xff0c;过滤的多了&#xff0c;感…

阿里巴巴开源的Spring Cloud Alibaba手册在GitHub上火了

“微服务架构经验你有吗&#xff1f;” 前段时间一个朋友去面试&#xff0c;阿里面试官一句话问倒了他。实际上&#xff0c;不在BAT这样的大厂工作&#xff0c;是很难接触到支撑千亿级流量微服务架构项目的。但也正是这种难得&#xff0c;让各个大厂都抢着要这样的人才&#x…

高校如何拿下数据分类分级这道“题”? 建设方案与实践来了

数据安全若一场“大考”&#xff0c;数据分类分级绝对是道“必答题”。 对高校而言&#xff0c;同样如此。作为高层次人才培养与科学研究的重要基地&#xff0c;高校既拥有高价值的科研等敏感数据&#xff0c;又涉及大量师生个人信息&#xff0c;无论是开展数据战略还是数据安全…

35岁以上的测试人员有多少?

今天在某论坛上看到一个有意思的问题&#xff1a;35岁以上的测试人员有多少&#xff1f; 细细一琢磨&#xff0c;为什么这位朋友会有这样的疑问呢&#xff1f;根据提问者的年龄划分&#xff0c;有以下两种可能&#xff1a; 35岁以下的提问者&#xff1a;想了解下35岁是否真如…

第八章 Electron 实现音乐播放器之爬虫播放音乐

一、介绍 &#x1f680; ✈️ &#x1f681; &#x1f682; 我在第五章已经有一篇Electron爬虫的文章&#xff0c;主要写的爬取图片资源的案例。这篇开始讲解如何到一个音乐网站爬取音乐资源&#xff0c;并且进行在线播放&#xff0c;下载等等。 那么什么是爬虫呢。百度百科上…

今日小课堂:怎么翻译音频

想象一下&#xff0c;你正在与外国朋友聊天&#xff0c;但是你们之间有语言障碍。不用担心&#xff01;现在有许多翻译语音识别工具可以帮助你轻松应对这种情况。通过这些工具&#xff0c;你可以将语音转换为文字&#xff0c;然后再将其翻译成你所需的语言。接下来&#xff0c;…

会声会影2023中文版本V26.0.0.136

会声会影2023中文版是一款功能强大的视频编辑软件、大型视频制作软件、专业视频剪辑软件。会声会影专业视频编辑处理软件&#xff0c;可以用于剪辑合并视频&#xff0c;制作视频&#xff0c;屏幕录制&#xff0c;光盘制作&#xff0c;视频后期编辑、添加特效、字幕和配音等操作…