STL——二叉搜索树

news2025/1/11 17:03:19

目录

二叉搜索树的概念

 ⼆叉搜索树的性能分析

 ⼆叉搜索树的插⼊

⼆叉搜索树的查找

⼆叉搜索树的删除

 中序遍历结果为升序序列


二叉搜索树的概念

⼆叉搜索树⼜称⼆叉排序树,它或者是⼀棵空树,或者是具有以下性质的⼆叉树
若它的左⼦树不为空,则左⼦树上所有结点的值都⼩于等于根结点的值
若它的右⼦树不为空,则右⼦树上所有结点的值都⼤于等于根结点的值
它的左右⼦树也分别为⼆叉搜索树
⼆叉搜索树中可以⽀持插⼊相等的值,也可以不⽀持插⼊相等的值,具体看使⽤场景定义,后续我 们学习map/set/multimap/multiset系列容器底层就是⼆叉搜索树,其中map/set不⽀持插⼊相等 值,multimap/multiset⽀持插⼊相等值

 ⼆叉搜索树的性能分析

最优情况下,⼆叉搜索树为完全⼆叉树(或者接近完全⼆叉树),其⾼度为: log 2 N
最差情况下,⼆叉搜索树退化为单⽀树(或者类似单⽀),其⾼度为: N
所以综合⽽⾔⼆叉搜索树增删查改时间复杂度为: O ( N )
中序遍历二叉搜索树得到一个升序序列
⼆分查找也可以实现 O (log 2 N ) 级别的查找效率,但是⼆分查找有两⼤缺陷:
1. 需要存储在⽀持下标随机访问的结构中,并且有序。 (有序数组
2. 插⼊和删除数据效率很低,因为存储在下标随机访问的结构中,插⼊和删除数据⼀般需要挪动数 据。

 ⼆叉搜索树的插⼊

1. 树为空,则直接新增结点,赋值给root指针
2. 树不空,按⼆叉搜索树性质,插⼊值⽐当前结点⼤往右⾛,插⼊值⽐当前结点⼩往左⾛,找到空位 置,插⼊新结点。
3. 如果⽀持插⼊相等的值,插⼊值跟当前结点相等的值可以往右⾛,也可以往左⾛,找到空位置,插 ⼊新结点。(要注意的是要保持逻辑⼀致性,插⼊相等的值不要⼀会往右⾛,⼀会往左⾛)
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);		// 1、若根为空,则就插入在此处
			return true;
		}
		Node* parent = nullptr;	// 使用parent保存父节点,用于后续链接
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)		// 2、不断递归寻找合适插入位置
                                              要满足左小右大的特点
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				cout << "所要插入的值已经存在" << endl;
				return false;
			}
		}
		cur = new Node(key);
		if (parent->_key > key)			// 3、找到之后使用parent和cur进行新建结点的接入
			parent->_left = cur;
		else
			parent->_right = cur;

		return true;
	}

⼆叉搜索树的查找

如果⽀持插⼊相等的值,意味着有多个x存在,⼀般要求查找中序的第⼀个x,如下图,查找3,要

找到1的右孩⼦的那个3返回。
    bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key == key)
				return true;
			else if (cur->_key > key)
				cur = cur->_left;
			else
				cur = cur->_right;
		}
		return false;
	}

⼆叉搜索树的删除

父要接管删除节点的剩余结点

若cur的左为空,那么父节点要处理cur的右子树

若cur的右为空,那么父节点要处理cur的左子树

若cur的左右均不为空,那么就找cur右子树的最左结点进行接管,最后处理最左节点的右子树

三种情况,最终处理时又都有俩种 孩子在左、在右的情况。

	bool Erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)	// 遍历搜索树,寻找需要删除的结点位置
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				// 此时找到了需要删除的结点位置,进行删除操作
				// 我的左为空,父亲处理我的右
				if (cur->_left == nullptr)
				{
					if (cur == _root) // 左为空,并且cur为根
						_root = cur->_right;

					if (cur == parent->_right)
						parent->_right = cur->_right;
					else if (cur == parent->_left)
						parent->_left = cur->_right;

					delete cur;
				}
				// 我的右为空,父亲处理我的左
				else if (cur->_right == nullptr)
				{
					if (cur == _root)// 右为空,并且cur为根
						_root = cur->_left;

					if (cur == parent->_right)
						parent->_right = cur->_left;
					else
						parent->_left = cur->_left;

					delete cur;
				}
				else
				{
					// 左右均不为空,需要找右子树的最左(最小)结点来接管我的位置
					Node* minRightNode = cur->_right; //通过minRightNode 和 minRightParent
					Node* minRightParent = cur;	      //找右子树最左节点

					while (minRightNode->_left)  
					{
						minRightParent = minRightNode;
						minRightNode = minRightNode->_left; // 一直向最左移动,注意保存父节点位置
					}

					cur->_key = minRightNode->_key;			// 将结点的_key值进行替换即可

					if (minRightParent->_right != minRightNode)		// 处理minRightNode结点,只有俩种情况,一种minRightNode是minRightParent的左孩子、另一种是右孩子
						minRightParent->_left = minRightNode->_right;	// 如果minRightNode是左孩子,那么minRightParent要接管它的右子树
					else
						minRightParent->_right = minRightNode->_right;	// 如果minRightNode是右孩子,那么minRightParent要接管它的左子树
					delete minRightNode;
				}
				return true;
			}
		}
		return false;
	}

 中序遍历结果为升序序列

 最开始没有写 InOrder() 函数,在main中进行调用时发生了 t.InOrder(??) 无法给左式括号内传参的问题,所以又加入了一个InOrder 和 _InOrder()俩个递归函数嵌套的形式。

template<class K>
class BSTree
{
	typedef BSTNode<K> Node;
public:	
    void InOrder()
	{
		_InOrder(_root);  
		cout << endl;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
	Node* _root = nullptr;
};

 

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

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

相关文章

【文件I/O】UNIX文件基础

IO编程的本质是通过 API 操作 文件。 什么是 IO I - Input 输入O - Output 输出 这里的输入和输出都是站在应用&#xff08;运行中的程序&#xff09;的角度。外部特指文件。 这里的文件是泛指&#xff0c;并不是只表示存在存盘中的常规文件。还有设备、套接字、管道、链接…

VS调试MFC进入系统源代码配置

调试MFC代码有时候能进入MFC的源代码,有时候不能.之前一直没有深入研究.后面经过查资料发现每次调试必能进入源代码的配置.很简单,只需要3步. 1.打开工具->选项->调试->符号,勾选Microsoft符号服务器. 2.打开项目->属性->配置属性->常规,MFC的使用修改成&qu…

车载网络:现代汽车的数字心跳

在汽车领域&#xff0c;“智能汽车”一词毫不夸张。如今的汽车已不再是原始的机械工程&#xff0c;而是通过先进的车载网络无缝连接的精密数字生态系统。这些滚动计算机由复杂的电子控制单元(ECU)网络提供动力&#xff0c;ECU是负责管理从发动机性能到信息娱乐系统等一切事务的…

mycat介绍与操作步骤

文章目录 1.分库分表2.mycat 入门2.1 概述2.2 案例&#xff1a;水平分表1&#xff09;准备工作2&#xff09;配置3&#xff09;启动并测试 3.mycat 配置详解3.1 schema.xml3.2 rule.xml3.3 server.xml 4.mycat 分片&#xff1a;垂直拆分1&#xff09;准备工作2&#xff09;配置…

【Python】Python之Selenium基础教程+实战demo:提升你的测试+测试数据构造的效率!

这里写目录标题 什么是Selenium&#xff1f;Selenium基础用法详解环境搭建编写第一个Selenium脚本解析脚本脚本执行结果常用的元素定位方法常用的WebDriver方法等待机制 Selenium高级技巧详解页面元素操作处理弹窗和警告框截图和日志记录多窗口和多标签页操作 一个实战的小demo…

Apache XMLBeans 一个强大的 XML 数据处理框架

Apache XMLBeans 是一个用于处理 XML 数据的 Java 框架&#xff0c;它提供了一种方式将 XML Schema (XSD) 映射到 Java 类&#xff0c;从而使得开发者可以通过强类型化的 Java 对象来访问和操作 XML 文档。下面将以一个简单的案例说明如何使用 Apache XMLBeans 来解析、生成和验…

带格式 pdf 翻译

支持 openAI 接口&#xff0c;国内 deepseek 接口兼容 openAI 接口&#xff0c; deepseek api 又非常便宜 https://pdf2zh.com/ https://github.com/Byaidu/PDFMathTranslate

ubuntu22.04降级安装CUDA11.3

环境&#xff1a;主机x64的ubuntu22.04&#xff0c;原有CUDA12.1&#xff0c;但是现在需要CUDA11.3&#xff0c;本篇文章介绍步骤。 一、下载CUDA11.3的run文件 下载网址&#xff1a;https://developer.nvidia.com/cuda-11-3-1-download-archive?target_osLinux&target_…

9 异常

如果你希望在软件调试上有所突破,或者想了解如何通过异常进行反调试,或者想自己写一个调试器,那么就必须要深入了解异常,异常与调试是紧密相连的,异常是调试的基础。 异常产生后,首先是要记录异常信息(异常的类型、异常发生的位置等),然后要寻找异常的处理函数,我们…

springBoot整合ELK Windowsb版本 (elasticsearch+logstash+kibana)

springBoot整合ELK Windowsb版本 【elasticsearchlogstashkibana】 下载软件启动服务1、elasticsearch2、kibana3、logstash 集成springboot1、添加依赖2、在logback.xml添加相关配置3、修改logstash 配置4、重启logstash 最后测试 下载软件 elasticsearch 官网 https://www.…

详解Sonar与Jenkins 的集成使用!

本文阅读前提 本文假设读者熟悉Jenkins和SonarQube的基础操作。 核心实现功能 Jenkins中运行的job来调用SonarScanner&#xff0c;最后可实现测试结果与SonarQube中同步查看。 Jenkins中安装Sonar相关插件 配置Sonarqube Dashboard>Manage Jenkins>Systems 指定son…

鸿蒙面试 2025-01-10

写了鉴权工具&#xff0c;你在项目中申请了那些权限&#xff1f;&#xff08;常用权限&#xff09; 位置权限 &#xff1a; ohos.permission.LOCATION_IN_BACKGROUND&#xff1a;允许应用在后台访问位置信息。 ohos.permission.LOCATION&#xff1a;允许应用访问精确的位置信息…

php 使用simplexml_load_string转换xml数据格式失败

本文介绍如何使用php函数解析xml数据为数组。 <?php$a <xml><ToUserName><![CDATA[ww8b77afac71336111]]></ToUserName><FromUserName><![CDATA[sys]]></FromUserName><CreateTime>1736328669</CreateTime><Ms…

【多空资金博弈】综合副图指标,资金做多线,短线做多雷达,中长线共振,大资金进场会涨等技术信号

如上图&#xff0c;副图指标【多空资金博弈】&#xff0c;红线做多资金线&#xff0c;绿色线为做空资金线&#xff0c;紫色柱线为短线做多雷达信号&#xff0c;紫色圆柱叠加文字为大资金进场信号&#xff0c;堆量柱线和紫色空心柱线为底部吸筹建仓信号&#xff0c;三条横向虚线…

Win11家庭版转专业版

Win11家庭版转专业版&#xff08;亲测有效&#xff09; 第一步 【断网】输入这个密钥&#xff1a; R8NJ8-9X7PV-C7RCR-F3J9X-KQBP6 第二步 点击下一步会自动重启 第三步 【联网】输入这个密钥&#xff1a; F3NWX-VFMFC-MHYYF-BCJ3K-QV66Y 注意 两次输入密钥的地方一致 …

【云商城】高性能门户网构建

第3章 高性能门户网构建 网站门户就是首页 1.OpenResty 百万并发站点架构 ​ 1).OpenResty 特性介绍 ​ 2).搭建OpenResty ​ 3).Web站点动静分离方案剖析 2.Lua语法学习 ​ 1).Lua基本语法 3.多级缓存架构实战 ​ 1).多级缓存架构分析 用户请求网站&#xff0c;最开始…

上海亚商投顾:沪指探底回升微涨 机器人概念股午后爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 市场全天探底回升&#xff0c;沪指盘中跌超1.6%&#xff0c;创业板指一度跌逾3%&#xff0c;午后集体拉升翻红…

计算机毕业设计Python机器学习农作物健康识别系统 人工智能 图像识别 机器学习 大数据毕业设计 算法

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

游戏语音的历史是什么样的?

游戏中&#xff0c;离不开游戏社交。 无社交&#xff0c;不游戏&#xff0c;大家都深知社交在游戏体验中的重要性。 游戏语音的发展史是怎么样的&#xff1f;问了下AI&#xff0c;给我的回答是 早期阶段 1970年代&#xff1a;1970年代出现了第一个语音游戏“尤瓦尔的冒险”&am…

《解锁图像的语言密码:Image Caption 开源神经网络项目全解析》

《解锁图像的语言密码&#xff1a;Image Caption 开源项目全解析》 一、开篇&#xff1a;AI 看图说话时代来临二、走进 Image Caption 开源世界三、核心技术拆解&#xff1a;AI 如何学会看图说话&#xff08;一&#xff09;深度学习双雄&#xff1a;CNN 与 RNN&#xff08;二&a…