二叉搜索树(BST)

目录

一、概念

二、代码实现

1.框架

2.查找

3.插入

4.删除

5.递归的写法

 三、应用


一、概念

  

二、代码实现

1.框架

#pragma once

namespace utoKey
{
	//结点
	template<class K>
	struct BinarySearchTreeNode
	{
		//结点的typedef
		typedef BinarySearchTreeNode Node;
		//
		Node* _left;
		Node* _right;

		K _key;
		//结点的构造函数,new操作时要调用
		BinarySearchTreeNode(const K& key)
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
		{}
	};

	//BST
	template<class K>
	class BinarySearchTree
	{
		typedef BinarySearchTreeNode Node;

	public:
		bool Find(const K& key);//查找
		bool Insert(const K& key);//插入
		bool Erase(const K& key);//删除
		void InOrder();//中序遍历,结果是升序

	private:
		Node* _root;
	};

}

2.查找

        遍历整个BST,基于BST的特性,如果要查找的值key大于当前cur的值,则在右子树查找,反之,则在左子树查找。

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

		}

3.插入

         插入一定是插入到了空结点,即通过BST的特点确定的当前cur为空的地方。插入的第一个结点,直接插入即可。

        a.树为空,则直接新增节点,赋值给root 指针
        b.树不空,按二叉搜索树性质查找插入位置,插入新节点
        bool Insert(const K& key)//插入
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					//说明要插入的值已经存在了,提升插入失败
					return false;
				}
			}
			cur = new Node(key);
			if (cur->_key > parent->_key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;

		}

4.删除

        删除分为以下几种情况:

        1.要删除的结点是叶子结点

        2.要删除的结点只有一个子树

        3.要删除的结点有左右两个子树。

  • 如果是1,比如删除上图的4,则直接删除叶子结点即可
  • 如果是2,比如删除上图的14,则把子树换到删除掉的位置,让10的右子树指向14的左子树
  • 如果是3,比如删除3,则要采用替换算法,具体操作是:

        a.找到左子树的最右结点(左子树最大)或者右子树的最左结点(右子树最小)

        b.将找到的值替换到要删除的位置,转换为要删除右子树最左结点(或者左子树最右结点)

         情况1、2可以看作同一种,比如删除叶子结点后,叶子结点的父指向为空。

        如果当作情况2,其实等于父指向要删除结点的非空子树,而此时该非空子树为空。

        情况3采用替换算法

实现思路        

        1.如果树为空,删除失败,返回空。

        2.查找到要删除的结点

        3.找到后分情况删除:

  • 如果当前结点左子树为空:

        判断当前结点是父的左还是右,如果是左,则父->左 = 结点->右;如果是右,则父->右 = 结点->左

        存在一种特殊情况,就是删除左子树为空的根结点,此时父和要删除的结点为同一个。

则让非空子树成为新树即可。

  • 如果当前结点右子树为空:

        同上

  • 如果左右子树都不为空:

        a.先找到右子树的最左结点Rightmin,同时记住该结点的父亲

        b.替换值

        c.判断Rightmin是左子还是右子,Rightmin一定没有左孩子,让父指向Rightmin的右子

        bool Erase(const K& key)//删除
		{
			if (_root == nullptr)
			{
				return false;
			}
			Node* cur = _root;
			Node* parent = _root;

			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_left;
						}
						delete cur;
						return true;
					}
					else if(cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
						delete cur;
						return true;
					}
					else
					{
						Node* rightmin_parent = cur;
						Node* rightmin = cur->_right;
						while (rightmin->_left)
						{
							rightmin_parent = rightmin;
							rightmin = rightmin->_left;
						}
						cur->_key = rightmin->_key;
						if (rightmin_parent->_left == rightmin)
						{
							rightmin_parent->_left = rightmin->_right;
						}
						else
						{
							rightmin_parent->_right = rightmin->_right;
						}
						delete rightmin;
						return true;
					}
				}
			}
			return false;
		}

5.递归的写法

       //查找 bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;
			if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else
			{
				return true;
			}
		}
//插入
        bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _InsertR(root->_right, key);
			}
			else
			{
				return false;
			}
		}
//删除
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else
			{
				Node* del = root;
				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else
				{
					Node* rightmin = root->_right;
					while (rightmin->_left)
					{
						rightmin = rightmin->_left;
					}
					swap(rightmin->_key, root->_key);

					return _EraseR(root->_right, key);
				}
				delete del;
				return true;
			}
		}

 三、应用

        1. K 模型: K 模型即只有 key 作为关键码,结构中只需要存储 Key 即可,关键码即为需要搜索到 的值 。输出结果为bool类型,表示是否找到。
        比如:给一个单词 word ,判断该单词是否拼写正确 ,具体方式如下:
        以词库中所有单词集合中的每个单词作为key ,构建一棵二叉搜索树 ,在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
        比如:小区的汽车进出,是否是该小区人员,判断是否允许进出。
        2. KV 模型:每一个关键码 key ,都有与之对应的值 Value ,即 <Key, Value> 的键值对 。该种方 式在现实生活中非常常见:
        比如英汉词典就是英文与中文的对应关系 ,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文 <word, chinese> 就构成一种键值对; 再比如统计单词次数 ,统计成功后,给定单词就可快速找到其出现的次数, 单词与其出 现次数就是 <word, count> 就构成一种键值对
        比如商场停车场,从停车场出去时,通过查找key即车牌找到记录<key,value>,value可能表示进入停车场的时间。

namespace utoKeyValue
{
	//结点
	template<class K,class V>
	struct BinarySearchTreeNode
	{
		//结点的typedef
		typedef BinarySearchTreeNode<K,V> Node;
		//
		Node* _left;
		Node* _right;

		K _key;
		V _value;
		//结点的构造函数,new操作时要调用
		BinarySearchTreeNode(const K& key,const V& value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			,_value(value)
		{}
	};

	//BST
	template<class K,class V>
	class BinarySearchTree
	{
		typedef BinarySearchTreeNode<K,V> Node;
	public:
		BinarySearchTree() = default;
		~BinarySearchTree()
		{
			Destory(_root);
		}
		
		Node* Find(const K& key)//查找
		{
			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;

		}
		bool Insert(const K& key,const V& value)//插入
		{
			if (_root == nullptr)
			{
				_root = new Node(key,value);
				return true;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					//说明要插入的值已经存在了,提升插入失败
					return false;
				}
			}
			cur = new Node(key,value);
			if (cur->_key > parent->_key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;

		}
		
	private:
		Node* _root = nullptr;
		void Destory(Node* root)
		{
			if (root == nullptr)
				return;
			Destory(root->_left);
			Destory(root->_right);
			delete root;
		}
	};

}

 

 

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

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

相关文章

利用pg_rman进行备份与恢复操作

文章目录 pg_rman简介一、安装配置pg_rman二、创建表与用户三、备份与恢复 pg_rman简介 pg_rman 是 PostgreSQL 的在线备份和恢复工具。类似oracle 的 rman pg_rman 项目的目标是提供一种与 pg_dump 一样简单的在线备份和 PITR 方法。此外&#xff0c;它还为每个数据库集群维护…

AIGC时代,“人”的核心价值在何处?

随着科技的浪潮汹涌向前&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;已悄然渗透至我们生活的每一个角落&#xff0c;从创意设计到信息传播&#xff0c;其影响力与变革力愈发显著。在这一由算法驱动的新纪元里&#xff0c;人类社会运作模式、学习途径及职业形态均…

眼动追踪技术 | 眼动的分类和模型

摘要 灵长类动物用于调整中央凹位置的正常眼动&#xff0c;几乎都可以归结为五种基本类型的组合&#xff1a;扫视、平稳追踪、聚散、前庭眼震和生理性眼震(与注视相关的微小运动)。聚散运动用于将双眼聚焦于远处的目标(深度知觉)。其他运动(如适应和聚焦)指的是眼动的非位置变…

LMT加仿真,十一届大唐杯全国总决赛

这次省赛带了太多个省一了&#xff0c;并且很多都进入了国赛总决赛&#xff0c;具体可看下面的图片&#xff0c;只放了一部分。目前只有B组是只有一个商用设备赛也就是LMT&#xff0c;A组和高职组都是仿真实践赛加上商用设备赛。 针对商用设备赛有对应的资料&#xff…

【深度学习】第3章——回归模型与求解分析

一、回归分析 1.定义 分析自变量与因变量之间定量的因果关系&#xff0c;根据已有的数据拟合出变量之间的关系。 2.回归和分类的区别和联系 3.线性模型 4.非线性模型 5.线性回归※ 面对回归问题&#xff0c;通常分三步解决 第一步&#xff1a;选定使用的model&#xff0c;…

CFS三层内网渗透——第二层内网打点并拿下第三层内网(三)

目录 八哥cms的后台历史漏洞 配置socks代理 ​以我的kali为例,手动添加 socks配置好了&#xff0c;直接sqlmap跑 ​登录进后台 蚁剑配置socks代理 ​ 测试连接 ​编辑 成功上线 上传正向后门 生成正向后门 上传后门 ​内网信息收集 ​进入目标二内网机器&#xf…

SAP-SD同一物料下单价格确不同

业务说明&#xff1a; 业务部门反馈&#xff0c;同一物料下销售订单时&#xff0c;价格确不同。 那么这个价格是怎么取到的呢&#xff1f; 逻辑说明&#xff1a; 1、首先查看销售订单 可以看到相同物料价格是不同的&#xff0c;条件类型都是ZPR5&#xff0c;但是客户是不同…

相关款式1111

一、花梨木迎客松 1. 风速打单 发现只有在兄弟店铺有售卖 六月份成交订单数有62笔 2. 生意参谋 兄弟店铺商品访客数&#xff1a;3548&#xff0c;支付件数&#xff1a;95件 二. 竹节茶刷&#xff08;引流&#xff09; 1. 风速打单 六月订单数有165笔 兄弟&#xff1a;…

揭秘数据之美:【Seaborn】在现代【数学建模】中的革命性应用

目录 已知数据集 tips 生成数据集并保存为CSV文件 数据预览&#xff1a; 导入和预览数据 步骤1&#xff1a;绘制散点图&#xff08;Scatter Plot&#xff09; 步骤2&#xff1a;添加回归线&#xff08;Regression Analysis&#xff09; 步骤3&#xff1a;分类变量分析&…

Mall,正在和年轻人重新对话

【潮汐商业评论/原创】 结束了一下午的苦闷培训&#xff0c;当Cindy赶到重庆十字大道时&#xff0c;才发现十字路口上的巨大“飞行棋”在前两天就已经撤展了。 “来了又错过&#xff0c;就会觉得遗憾&#xff0c;毕竟这样的路口不多&#xff0c;展陈又不可能会返场。” 飞行棋…

藏文作文写作业推荐什么学习工具?《藏文翻译词典》App值得你使用,一款好用准确的藏语词汇查询辞典!

探索藏语的奥秘&#xff0c;体验藏族文化的魅力&#xff0c;尽在《藏文翻译词典》App。这款App是藏汉翻译的神器&#xff0c;也是藏语学习者的必备工具。在学习过程中遇到不会的藏语单词&#xff0c;可以使用《藏文翻译词典》App进行查询&#xff01; 主要特性&#xff1a; 藏…

SCT612404通道,高效高集成,摄像头模组电源集成芯片

集成三路降压变换器&#xff0c;1CH高压BUCK,2CH低压Buck >HVBuck1:输入电压4.0V-20V,输出电流1.2A,Voo300mV/500mV >LVBuck2:输入电压2.7V-5V,输出电流0.6A , 固定1.8V输出 ;LVBuck3:输λ2.7V-5V,输出电流1.2A,可设定固定输出&#xff1a; 1 . 1 V / 1 . 2 V / 1 . 3 …

Intellj idea无法启动

个人电脑上安装的是2024.01版本的intellj idea作为开发工具&#xff0c;引入了javaagent作为工具包 但是在一次invaliad cache操作后&#xff0c;intellj idea就无法启动了&#xff0c;双击无响应。 重装了idea后也无效&#xff08;这个是有原因的&#xff0c;下面会讲&#…

开发人员使用的10大主流任务进度管理工具

本文将分享10大优质任务管理软件&#xff1a;Worktile、PingCode、Asana、Todoist、ClickUp、HubSpot Task Management、Hitask、Smartsheet、ProjectManager、Microsoft To Do。 任务管理软件不仅帮助个人和团队跟踪日常任务&#xff0c;还优化了工作流程&#xff0c;确保项目…

Linux/Ubuntu访问局域网共享文件夹

文件夹中找到“Other Location”&#xff0c;输入“smb:IP地址/共享文件夹名称”&#xff0c;然后点击connect后者直接回车即可&#xff01; End&#xff01;

Redis 主从,哨兵,cluster集群

概述 主从复制 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。 主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。 缺陷&#xff1a;故障恢复无法自动化&#xff1b;写操作无法负载均衡&am…

联合查询(多表查询)

多表查询是对多张表的数据取笛卡尔积&#xff08;关联查询可以对关联表使用别名&#xff09; 数据准备 insert into classes(name, desc) values (计算机系2019级1班, 学习了计算机原理、C和Java语言、数据结构和算法), (中文系2019级3班,学习了中国传统文学), (自动化2019级5…

Mysql 的第二次作业

一、数据库 1、登陆数据库 2、创建数据库zoo 3、修改数据库zoo字符集为gbk 4、选择当前数据库为zoo 5、查看创建数据库zoo信息 6、删除数据库zoo 1&#xff09;登陆数据库。 打开命令行&#xff0c;输入登陆用户名和密码。 mysql -uroot -p123456 ​ 2&#xff09;切换数据库…

前端修改audio背景色

1.查看浏览器设置Show user agent shadow DOM是否打开 2.打开可以查看audio Dom /** 去掉默认的背景颜色 */ audio::-webkit-media-controls-enclosure{background-color:unset; } 3.效果图

浅谈OpenCV的多对象匹配透明图像的实现,以及如何匹配半透明控件

引子 OpenCV提供的templateMatch只负责将&#xff08;相关性等&#xff09;计算出来&#xff0c;并不会直接提供目标的对应坐标&#xff0c;一般来说我们直接遍历最高的相关度&#xff0c;就可以得到匹配度最高的坐标。但是这样一般只能得到一个坐标。在实际操作中&#xff0c;…