C/C++ 进阶(4)二叉搜索树

个人主页:仍有未知等待探索-CSDN博客

专题分栏:C++

一、概念

二叉搜索树,又称二叉排序树(中序是有序的)。

性质

1、非空左子树的键值小于根节点的键值

2、非空右子树的键值大于根节点的键值

空树也是二叉搜索树

二、操作

查找

查找,从根节点开始比较键值大小,如果比根节点的键值小,进左子树;比根节点的键值大,进右子树,然后继续进行比较。

插入

和查找差不多,先找到要插入的位置,然后进行插入即可。

// 要插入的值 val
void insert(const T& val)
{
    // 特判一下空树
	if (_root == nullptr)
	{
		node* tmp = new node(val);
		_root = tmp;
		return;
	}
    
    // 记得要记录一下插入位置的父节点
	node* parent = _root;
	node* cur = _root;
	while (cur)
	{
		if (cur->val > val)
		{
			parent = cur;
			cur = cur->left;
		}
		else
		{
			parent = cur;
			cur = cur->right;
		}
	}
	cur = new node(val);
	if (parent->val > val)
	{
		parent->left = cur;
	}
	else
	{
		parent->right = cur;
	}
}

删除

删除一个节点的情况比较复杂,分为四种情况。

a.删除的节点为叶子节点

如果要删除 13 这个叶子节点,我们只需要让 13 的父节点的左孩子指向nullptr。

如果要删除 7 这个叶子节点,我们需要让 7 的父节点的右孩子指向nullptr。

总结:让要删除节点的父节点的对应指针指向空。

对应指针:判断要删除的节点是其父节点的左孩子还是右孩子。如果是左孩子,父节点的左孩子指向空;如果是右孩子,父节点的右孩子指向空。

b.删除的节点只有左子树

如果要删除 14 这个节点,我们需要让 14 的父节点的对应指针指向 14 的左子树。

总结:让要删除节点的父节点的对应指针指向要删除节点的左子树。

c.删除的节点只有右子树

如果要删除 10 这个节点,我们需要让 10 的父节点指向 10 的右子树。

总结:让要删除节点的父节点的对应指针指向要删除节点的右子树。

d.删除的节点有左、右子树

对于这个这种情况,我们用替换法来进行删除。首先我们先找到要删除节点的右子树中的最小值,然后和要删除的节点进行值替换,最后我们要删除掉替换后的节点。

总结:先替换,然后删除替换后的节点(判断替换后的节点的类型,然后用对应的删除方法)

void erase(const T& val)
{
	node* cur = _root;
	node* parent = _root;
	
	while (cur)
	{
		if (cur->val > val)
		{
			parent = cur;
			cur = cur->left;
		}
		else if (cur ->val < val)
		{
			parent = cur;
			cur = cur->right;
		}
		else
		{
			if (cur->left == nullptr)
			{
				if (cur == _root)
				{
					_root = cur->right;
				}
				else if (parent->left == cur)
				{
					parent->left = cur->right;
				}
				else if (parent->right == cur)
				{
					parent->right = cur->right;
				}
				delete cur;
			}
			else if(cur->right == nullptr)
			{
				if (cur == _root)
				{
					_root = cur->left;
				}
				else if (parent->left == cur)
				{
					parent->left = cur->left;
				}
				else if (parent->right == cur)
				{
					parent->right = cur->left;
				}
				delete cur;
			}
			else
			{
				node* tmp = cur->right;
				node* tp = cur;
				while (tmp->left)
				{
					tp = tmp;
					tmp = tmp->left;
				}
				swap(cur->val, tmp->val);
				
				// 交换完,就相当是删除tmp,tmp的特点是左子树为空
				// 也相当于是删除没有左子树的节点

				if (tp->left == tmp)
					tp->left = tmp->right;
				else
					tp->right = tmp->right;

				delete tmp;

			}
		}
	}
}

总代码

#include <iostream>
#include <vector>
using namespace std;

template<class T>
struct BSTN
{
	BSTN(const T& e)
		:left(nullptr)
		,right(nullptr)
		,val(e)
	{}
	BSTN<T>* left;
	BSTN<T>* right;
	T val;
};

template<class T>
class BST
{
	typedef BSTN<T> node;
public:
	BST()
		:_root(nullptr)
	{}
	void insert(const T& val)
	{
		if (_root == nullptr)
		{
			node* tmp = new node(val);
			_root = tmp;
			return;
		}
		node* parent = _root;
		node* cur = _root;
		while (cur)
		{
			if (cur->val > val)
			{
				parent = cur;
				cur = cur->left;
			}
			else
			{
				parent = cur;
				cur = cur->right;
			}
		}
		cur = new node(val);
		if (parent->val > val)
		{
			parent->left = cur;
		}
		else
		{
			parent->right = cur;
		}
	}
	void inorder()
	{
		_inorder(_root);
	}
	bool find(const T& val)
	{
		node* cur = _root;
		while (cur)
		{
			if (cur->val > val)
			{
				cur = cur->left;
			}
			else if (cur->val < val)
			{
				cur = cur->right;
			}
			else
			{
				return true;
			}
		}
		return false;
	}
	void erase(const T& val)
	{
		node* cur = _root;
		node* parent = _root;
		
		while (cur)
		{
			if (cur->val > val)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur ->val < val)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				if (cur->left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->right;
					}
					else if (parent->left == cur)
					{
						parent->left = cur->right;
					}
					else if (parent->right == cur)
					{
						parent->right = cur->right;
					}
					delete cur;
				}
				else if(cur->right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->left;
					}
					else if (parent->left == cur)
					{
						parent->left = cur->left;
					}
					else if (parent->right == cur)
					{
						parent->right = cur->left;
					}
					delete cur;
				}
				else
				{
					node* tmp = cur->right;
					node* tp = cur;
					while (tmp->left)
					{
						tp = tmp;
						tmp = tmp->left;
					}
					swap(cur->val, tmp->val);
					
					// 交换完,就相当是删除tmp,tmp的特点是左子树为空
					// 也相当于是删除没有左子树的节点

					if (tp->left == tmp)
						tp->left = tmp->right;
					else
						tp->right = tmp->right;

					delete tmp;

				}
			}
		}
	}
private:
	void _inorder(node* root)
	{
		if (root == nullptr) return;

		_inorder(root->left);
		cout << root->val << " ";
		_inorder(root->right);
	}
private:
	node* _root;

};

三、K-V模型 

什么叫K-V模型?

其全称是key-value模型,是一种映射关系。一个key(关键字)对应一个value(值)。

比如:英语和汉语就是一种kv模型。stl库中的map也是一种kv模型,不过底层不是用二叉搜索树实现的。

代码

#include <iostream>
#include <vector>
using namespace std;

template<class K, class V>
struct BSTN
{
	typedef pair<K, V> PKV;
	BSTN(const PKV& e)
		:left(nullptr)
		,right(nullptr)
		,val(e)
	{}
	BSTN<K, V>* left;
	BSTN<K, V>* right;
	PKV val;
};

template<class K, class V>
class BST
{
	typedef pair<K, V> PKV;
	typedef BSTN<K, V> node;
public:
	BST()
		:_root(nullptr)
	{}
	void insert(const PKV& val)
	{
		if (_root == nullptr)
		{
			node* tmp = new node(val);
			_root = tmp;
			return;
		}
		node* parent = _root;
		node* cur = _root;
		while (cur)
		{
			if (cur->val.first > val.first)
			{
				parent = cur;
				cur = cur->left;
			}
			else
			{
				parent = cur;
				cur = cur->right;
			}
		}
		cur = new node(val);
		if (parent->val.first > val.first)
		{
			parent->left = cur;
		}
		else
		{
			parent->right = cur;
		}
	}
	void inorder()
	{
		_inorder(_root);
	}
	bool find(const K& val)
	{
		node* cur = _root;
		while (cur)
		{
			if (cur->val > val)
			{
				cur = cur->left;
			}
			else if (cur->val < val)
			{
				cur = cur->right;
			}
			else
			{
				return true;
			}
		}
		return false;
	}
	void erase(const PKV& val)
	{
		node* cur = _root;
		node* parent = _root;

		while (cur)
		{
			if (cur->val.first > val.first)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur ->val.first < val.first)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				if (cur->left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->right;
					}
					else if (parent->left == cur)
					{
						parent->left = cur->right;
					}
					else if (parent->right == cur)
					{
						parent->right = cur->right;
					}
					delete cur;
				}
				else if(cur->right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->left;
					}
					else if (parent->left == cur)
					{
						parent->left = cur->left;
					}
					else if (parent->right == cur)
					{
						parent->right = cur->left;
					}
					delete cur;
				}
				else
				{
					node* tmp = cur->right;
					node* tp = cur;
					while (tmp->left)
					{
						tp = tmp;
						tmp = tmp->left;
					}
					swap(cur->val, tmp->val);

					// 交换完,就相当是删除tmp,tmp的特点是左子树为空
					// 也相当于是删除没有左子树的节点

					if (tp->left == tmp)
						tp->left = tmp->right;
					else
						tp->right = tmp->right;

					delete tmp;

				}
			}
		}
	}
private:
	void _inorder(node* root)
	{
		if (root == nullptr) return;

		_inorder(root->left);
		cout << root->val.first << " " << root->val.second << endl;
		_inorder(root->right);
	}
private:
	node* _root;

};

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

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

相关文章

变压器励磁涌流MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 变压器励磁涌流的产生机理 1、变压器是电力系统的关键部分&#xff0c;在实际的 运行中&#xff0c;变压器需要进行相应的充电&#xff0c;而在充电的过 程中&#xff0c;就需要进行开合闸作业。在开合闸作业…

智慧楼宇:城市生活的新篇章

在城市的喧嚣与繁华中&#xff0c;楼宇不仅是我们工作与生活的场所&#xff0c;更是智慧科技发展的前沿阵地。当传统的建筑遇上智慧的火花&#xff0c;便诞生了令人瞩目的智慧楼宇。 山海鲸可视化搭建的智慧楼宇数字孪生系统 一、智慧楼宇&#xff0c;定义未来生活 智慧楼宇不…

vue3学习使用笔记

1.学习参考资料 vue3菜鸟教程&#xff1a;https://www.runoob.com/vue3/vue3-tutorial.html 官方网站&#xff1a;https://cn.vuejs.org/ 中文文档: https://cn.vuejs.org/guide/introduction.html Webpack 入门教程&#xff1a;https://www.runoob.com/w3cnote/webpack-tutor…

【linux】docker下nextcloud安装人脸识别插件2

接上文 【linux】docker下nextcloud安装人脸识别插件-CSDN博客 由于作者不再维护此插件&#xff0c;转而开发新的插件 recognize &#xff0c;因此同步更新插件使用教程。 1、下载人脸识别app&#xff1a;recognize Recognize - Apps - App Store - Nextcloud 2、将插件recog…

Java后端模拟面试 题集⑤

1.先作个自我介绍吧 面试官您好&#xff0c;我叫张睿超&#xff0c;来自湖南长沙&#xff0c;大学毕业于湖南农业大学&#xff0c;是一名智能科学与技术专业的统招一本本科生。今天主要过来面试贵公司的Java后端开发工程师岗位。 大学里面主修的课程是Java、Python、数字图像…

Linux自动挂载服务autofs讲解

1.产生原因 2.配置文件讲解 总结&#xff1a;配置客户端&#xff0c;先构思好要挂载的目录如&#xff1a;/abc/cb 然后在autofs.master中编辑&#xff1a; /abc&#xff08;要挂载的主目录&#xff09; /etc/qwe&#xff08;在这个文件里去找要挂载的副目录&#xff0c;这个名…

【C++】入门(二):引用、内联、auto

书接上回&#xff1a;【C】入门&#xff08;一&#xff09;&#xff1a;命名空间、缺省参数、函数重载 文章目录 六、引用引用的概念引用的使用场景1. 引用做参数作用1&#xff1a;输出型参数作用2&#xff1a;对象比较大&#xff0c;减少拷贝&#xff0c;提高效率 2. 引用作为…

LangGraph实战:可控的AI航空客服助手

上节课&#xff0c;我们定义了AI航空客服助手需要使用的一系列API接口工具&#xff0c;并定义了一个简单的零样本代理作为用户的助手。没看过的同学可以点击链接LangGraph实战&#xff1a;从零分阶打造人工智能航空客服助手查阅。这次我们将讲述&#xff0c;如何通过LangGraph的…

预编码算法(个人总结)

引言 预编码算法是现代无线通信系统中的关键技术&#xff0c;特别是在多输入多输出&#xff08;MIMO&#xff09;系统中。它们通过在发送端对信号进行处理&#xff0c;减少干扰并提高信道容量。这种技术广泛应用于5G、Wi-Fi和卫星通信系统中。本教程将详细介绍预编码算法的背景…

Redis 探索之旅(进阶)

目录 今日良言&#xff1a;从不缺乏从头开始的勇气 一、持久化 1、RDB 2、AOF 二、Redis 的事务 三、主从复制 四、哨兵模式 五、集群模式 六、缓存 七、分布式锁 今日良言&#xff1a;从不缺乏从头开始的勇气 一、持久化 持久化就是把数据存储在硬盘上&#xff0c;无…

鸿蒙开发接口媒体:【@ohos.multimedia.media (媒体服务)】

媒体服务 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 开发前请熟悉鸿蒙开发指导文档&#xff1a; gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 媒体子系…

C++青少年简明教程:While和Do-while循环语句

C青少年简明教程&#xff1a;While和Do-while循环语句 C的while和do-while语句都是循环控制语句&#xff0c;用于重复执行一段代码。while语句在循环开始前检查循环条件&#xff0c;而do-while语句在循环结束后检查循环条件。 使用while循环时&#xff0c;如果需要在每次迭代前…

【SpringMVC】_设置响应状态码与Header

目录 1. 设置响应状态码 2. 设置响应Header 2.1 设置Content-Type 2.1.1 不使用RequestMapping的produce属性 2.1.2 使用RequestMapping的produce属性 2.2 设置/新增其他Header 1. 设置响应状态码 Spring是基于servlet实现的&#xff0c;设置HTTP响应的状态码可以通过se…

Flink实现实时异常登陆监控(两秒内多次登陆失败进行异常行为标记)

Flink实现异常登陆监控&#xff08;两秒内多次登陆失败进行异常行为标记&#xff09; 在大数据处理领域&#xff0c;Apache Flink 是一个流行的开源流处理框架&#xff0c;能够高效处理实时数据流。在这篇博客中&#xff0c;我们将展示如何使用 Apache Flink 从 MySQL 中读取数…

docker compose完成简单项目部署

1. 项目环境 centos7 docker mysql redis ruoyi项目 ruoyi项目链接&#xff1a;https://gitee.com/y_project/RuoYi-Vue.git 2. 进行项目前后端代码打包 后端打包&#xff1a; 修改mysql连接的相关配置文件 RuoYi-Vue/ruoyi-admin/src/main/resources/application-dru…

Scroll 上的明星项目Pencils Protocol ,缘何被严重低估?

近日&#xff0c;完成品牌升级的 Pencils Prtocol 结束了 Season 2 并无缝开启了 Season 3&#xff0c;在 Season 3 中&#xff0c;用户可以通过质押系列资产包括 $ETH、$USDT、$USDC、$STONE 、$wrsETH、$pufETH 等来获得可观收益&#xff0c;并获得包括 Scroll Marks、 Penci…

深入理解flask规则构建与动态变量应用

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、Flask规则基础 1. 静态规则与动态规则 2. 规则语法与结构 三、动态变量应用…

chrome谷歌浏览器开启Gemini Nano模型

前提 确保您的操作系统语言设置为英语(美国) 可能还需要将 Chrome 浏览器的语言更改为英语(美国)。 下载dev或Canary版本Chrome Chrome Canary Chrome Dev 注意:确认您的版本高于 127.0.6512.0。 其中一个Chrome版本不行就切换另外一个版本 绕过性能检查 Tab输入: …

【高校科研前沿】南大王栋、吴吉春教授团队在深度学习助力水库生态调度和优化管理方面取得新进展,成果以博士生邱如健为一作发表于水环境领域国际权威期刊

1.文章简介 论文名称&#xff1a;Integration of deep learning and improved multi-objective algorithm to optimize reservoir operation for balancing human and downstream ecological needs 第一作者及单位&#xff1a;邱如健&#xff08;博士生 南京大学&#xff09;…

电商物流查询解决方案助力提升消费者体验

截至2023年12月&#xff0c;中国网络购物用户规模达9.15亿人&#xff0c;占网民整体的83.8%。这一庞大的数字不仅展现了电子商务的蓬勃发展&#xff0c;也标志着数字零售企业营销战略的转变——从以产品和流量为核心&#xff0c;到用户为王的新阶段。因此&#xff0c;提升消费者…