C++进阶(3): 二叉搜索树

news2024/12/30 15:48:04

二叉搜索树的概念

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

  • 若它的左子树不为空,则左子树上所有的节点的值都小于等于 根节点的值
  • 若它的右子树不为空,则右子树上所有的节点的值都大于等于 根节点的值
  • 它的左右子树也分别为二叉搜索树
  • 二叉搜索树可以支持插入相同的值(multimap/multiset),也可以不支持插入相等的值(map/set)

二叉搜索树的性能分析

  1. 最优的情况:二叉搜索树为完全二叉树(或者接近完全二叉树),它的高度是:O(log2 N)
  2. 最坏的情况:二叉搜索树退化为单支树(类似于单支),它的高度是:O(N/2)

所以综合而言二叉搜索树增删改时间复杂度是:O(N)
但是这样的效率显然无法满足我们的需求,后续的博客我将介绍二叉搜索树的变形,平衡二叉树AVL树和红黑树,才能适用于我们在内存在存储和搜索数据。

最好的情况
最坏的情况

二叉搜索树的插入

不用递归,采用更简单的循环

插入的具体步骤如下:

  1. 树为空,则直接新增节点,赋值给root指针
  2. 树不为空,按照二叉搜索树性质,插入值比当前节点大往右走,插入值比当前节点小往左走,找到空位置,插入新节点。
  3. 如果支持插入相同的值,插入值跟当前节点相等的值可以往右走,也可以往走左走,找到空位置,插入新节点(要注意保持逻辑的一致性,插入相同的值不要一会向右走,一会向左走)

二叉搜索树的查找

  1. 从根节点开始比较,查找x,x比根节点的值大则向右边走查找,x比根节点的值小则向左边走查找
  2. 最多查找高度次,走到空时,还没找到x,那么这个值在这个二叉搜索树中不存在
  3. 如果不支持插入相同的值,则找到x后可以直接返回
  4. 如果支持插入相同的值,则意味着可能存在多个x,一般要求查找中序的第一个x

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回false
如果查找元素存在则分为以下四个情况进行分别处理:(假设要删除的节点为N)(分类依据:孩子的个数)

  1. 要删除的节点N左右孩子均为空(即N为叶子节点)
  2. 要删除的节点N左孩子为空,右孩子不为空
  3. 要删除的节点N左孩子不为空,右孩子为空
  4. 要删除的节点N左右孩子均不为空

对于以上四种情况的解决办法:

  1. 把N节点的父亲对应孩子指针指向空,直接删除N节点(1可以和2.3一起处理)
  2. 把N节点的父亲对应孩子指针指向N的右孩子,直接删除N节点
  3. 把N节点的父亲对应孩子指针指向N的左孩子,直接删除N节点
  4. 无法直接删除N节点,因为N的两个孩子无处安放,只能采用替代法将其删除:将N左子树的最大值的节点R(最右节点)或者N右子树的最小值的节点R(最左节点)代替N,因为这两个节点中任意一个,放到N的节点,都满足二叉搜索树的规则。代替N的意思就是N和R的两个节点的值交换,转而变成删除R节点,R节点符合情况2或者情况3,可以直接删除
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

实现代码

template<class K>

struct BSTNode
{
	K _key;
	BSTNode<K>* _left;
	BSTNode<K>* _right;

	BSTNode(const K& key)
		:_key(key)
		, _left(nullptr)
		, _right(nullptr)
	{}
};

//搜索二叉树:
template<class K>

class BSTree
{
	typedef BSTNode<K> Node;

public:

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

		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
			{
				return false;//不能插入相同的值
			}
		}

		cur = new Node(key);
		if (parent->_key > key)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}

		return true;
	}

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

			return false;
		}
	}

	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 (nullptr == cur->_left)
				{
					if (parent == nullptr)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else if (parent->_right = cur)
						{
							parent->_right = cur->_right;
						}
					}

					delete cur;
					return true;
				}
				else if (nullptr == cur->_right)
				{
					if (nullptr == parent)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}

					delete cur;
					return true;
				}
				else
				{
					Node* leftMaxP = cur;
					Node* leftMax = cur->_left;

					while (leftMax->right)
					{
						leftMaxP = leftMax;
						leftMax = leftMax->_right;
					}

					cur->_key = leftMax->_key;

					if (leftMaxP->_left == leftMax)
					{
						leftMaxP->_left = leftMax->_left;
					}
					else
					{
						leftMaxP->_right = leftMax->_left;
					}

					delete leftMax;
					return true;
				}
			}

		}

		return false;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

private:
	Node* _root = nullptr;
};

二叉搜索树key和key/value使用场景

key搜索场景

只有key作为关键码,在二叉树结构中只需要存储key即可,搜索场景只需要判断key在不在。key的搜索场景支持二叉树的增删查,而不支持二叉树的修改,会破坏二叉树的结构。

key/value搜索场景

每一个关键码key,都有与它对应的值value,value可以是任何类型对象。树的结构中(每个节点)除了需要存储key,还需要存储value,增/删/查还是以key为关键字走二叉树的规则进行比较,可以快速找到与key对应的value。并且key/value的搜索场景支持了二叉树的修改,但是仍然不可以修改key,只可以修改value。

key/value二叉搜索树代码实现

	template<class K, class V>

	struct BSTNode
	{
		K _key;
		V _value;
		BSTNode<K, V>* _left;
		BSTNode<K, V>* _right;

		BSTNode(const K& key, const K& value)
			:_key(key)
			, _value(value)
			, _left(nullptr)
			, _right(nullptr)
		{}
	};

	//搜索二叉树:
	template<class K, class V>

	class BSTree
	{
		typedef BSTNode<K, V> Node;
	public:

		BSTree() = default;

		BSTree(const BSTree<K, V>& m)
		{
			_root = Copy(m._root);
		}

		BSTree<K, V>& operator=(BSTree<K, V> m)
		{
			swap(_root, m._root);
			return *this;
		}

		~BSTree()
		{
			Destroy(_root);
			_root = nullptr;
		}

		bool Insert(const K& key, const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key, value);
				return true;
			}

			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
				{
					return false;
				}
			}

			cur = new Node(key, value);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}

			return true;
		}

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

			return nullptr;
		}

		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 (nullptr == cur->_left)
					{
						if (nullptr == parent)
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
						return true;
					}
					else if (nullptr == cur->_right)
					{
						if (nullptr == parent)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
						return true;
					}
					else
					{
						Node* rightMinP = cur;
						Node* rightMin = cur->_right;
						while (rightMin->_left)
						{
							rightMinP = rightMin;
							rightMin = rightMin->_left;
						}

						cur->_key = rightMin->_key;

						if (rightMinP->_left == rightMin)
						{
							rightMinP->_left = rightMin->_right;
						}
						else
						{
							rightMinP->_right = rightMin->_right;

						}

						delete rightMin;
						return true;
					}
				}
			}
			return false;
		}

		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

	private:

		void _InOrder(Node* root)
		{
			if (nullptr == root)
			{
				return;
			}

			_InOrder(root->_left);
			cout << root->_key << ':' << root->_value << endl;
			_InOrder(root->right);
		}

		void Destroy(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}

			Destroy(root->_left);
			Destroy(root->_right);

			delete root;
		}

		Node* Copy(Node* root)
		{
			if (nullptr == root)
			{
				return nullptr;
			}

			Node* newRoot = new Node(root->_key, root->_value);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);

			return newRoot;
		}
	private:
		Node* _root = nullptr;
	};

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

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

相关文章

时序必读论文15|TimeXer:通过外部变量增强Transformer在时间序列预测中的能力

论文标题&#xff1a;TimeXer: Empowering Transformers for Time Series Forecasting with Exogenous Variables 论文链接&#xff1a;https://arxiv.org/abs/2402.19072 前言 仅仅关注内生变量&#xff0c;通常不足以保证准确的预测&#xff0c;外部序列可以为内生变量提供…

MongoDB 副本集搭建 【docker compose + 本机部署】【建议收藏起来】

什么是副本集 MongoDB副本集&#xff08;Replica Set&#xff09;是一种数据冗余和故障恢复机制&#xff0c;它允许你维护相同数据的一个或多个副本&#xff0c;并提供自动故障转移和数据恢复能力。副本集是一个包含多个MongoDB实例&#xff08;通常称为成员&#xff09;的集合…

Linux学习笔记(一):Linux学习环境的安装及远程连接工具的使用

Linux学习笔记&#xff08;一&#xff09;&#xff1a;Linux学习环境的安装及远程连接工具的使用 本地安装虚拟机 1.安装VMWare(点击图片跳转下载&#xff09; 2.安装CentOS7光盘源(点击图片下载&#xff09; 3.安装 一路点下一步 点击确认 等待不要动,可能有点久,直到这个…

【内网渗透】最保姆级的春秋云镜Flarum打靶笔记

目录 flag1 flag3 flag4​ flag2 flag1 扫外网 打的是flarum论坛&#xff0c;p牛之前有写过phar反序列化的利用&#xff1a; 从偶遇Flarum开始的RCE之旅 rockyou.txt爆出administrator/1chris&#xff0c;登录 用这个工具生成phar包 https://github.com/ambionics/p…

【Python报错已解决】IndexError: index 0 is out of bounds for axis 1 with size 0

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

springboot整合seata

一、准备 docker部署seata-server 1.5.2参考&#xff1a;docker安装各个组件的命令 二、springboot集成seata 2.1 引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>&…

helm部署ingress-nginx

helm部署ingress-nginx 19/100 保存草稿 发布文章 sj1163739403 未选择任何文件 ingress-nginx是管理南北向流量很好的一个工具&#xff0c;可以避免在云端频繁对LB进行配置&#xff0c;通过Label也可以实现让ingress-nginx都部署在指定的NodeGroup 一、helm安装ingress-nginx…

数据管理_DM的实现

DataManager 的实现 DataManager 是数据库系统中的核心组件&#xff0c;负责管理底层数据的访问、修改和事务处理。它是 DM 层直接对外提供方法的类&#xff0c;用来对上层其他模块提供数据操作的API的,同时也实现了对 DataItem 对象的缓存管理。DataItem 存储的 key 是由页号…

使用root账号ssh登录虚拟机ubuntu

在C:\Users\Administrator\.ssh目录下的config中&#xff0c;添加ubuntu会在根目录中&#xff0c;建立一个root文件夹。在该文件夹中建一个.ssh目录。像免密登录ubuntu设置中&#xff0c;把公钥考进去。在vscode中打开文件夹中选择要打开的文件夹&#xff0c;就可以不需要在ubu…

fiddler抓包14_自动重定向

课程大纲 重定向&#xff08;Redirect&#xff09;&#xff1a;将网络请求重新定向到其他位置。 按重定向方式分为&#xff1a;网页重定向、域名重定向、接口重定向…… 按持续时间分为&#xff1a;永久重定向、临时重定向。 ① 永久重定向&#xff0c;HTTP 301&#xff08;Mov…

Python或R时偏移算法实现

&#x1f3af;要点 计算单变量或多变量时序距离&#xff0c;使用欧几里得、曼哈顿等函数量化不同时序差异。量化生成时序之间接近度相似性矩阵。使用高尔距离和堪培拉距离等相似度测量。实现最小方差匹配算法&#xff0c;绘制步进模式的图形表示。其他语言包算法实现。 &…

D24【 python 接口自动化学习】- python 基础之判断与循环

day24 while循环 学习日期&#xff1a;20241001 学习目标&#xff1a;判断与循环--34 while循环&#xff1a;需要多次重复执行某段程序时&#xff0c;怎么做&#xff1f; 学习笔记&#xff1a; 循环结构简介 while循环语法 代码实现 循环的退出方法 执行出错&#xff0c;异…

复合系统推文

今天推出的是复合系统协调度这个模型的工具。 参考文献:《“干线公路—城市结点”复合系统协调度分析模型》 复合系统整体协调度模型以协同学的序参量原理和役使原理为基础&#xff0c;模型的参变量选择是模型合理性的前提&#xff0c; 重点选择在整个系统发展演变过程中起主…

CSP-J模拟赛(1)补题报告

前言&#xff1a; 1.交替出场&#xff08;alter) &#xff1a;10 2.翻翻转转&#xff08;filp)&#xff1a;0 3.方格取数&#xff08;square&#xff09;&#xff1a;0 4.圆圆中的方方&#xff08;round)&#xff1a;0 总结一下&#xff1a; 第一次考&#xff0c;没爆零就是胜…

锂电池SOC估计 | Matlab基于BP神经网络的锂电池SOC估计

锂电池SOC估计 | Matlab基于BP神经网络的锂电池SOC估计 目录 锂电池SOC估计 | Matlab基于BP神经网络的锂电池SOC估计预测效果基本描述程序设计参考资料 预测效果 基本描述 锂电池SOC估计 | Matlab基于BP神经网络的锂电池SOC估计 运行环境Matlab2023b及以上。 要实现基于BP神…

鼓组编曲:鼓编写技巧之进鼓加花编写

为了方便快速查阅和运用一些教程笔记&#xff0c;个人记性有时可能不是特别好&#xff0c;所以只能疯狂做笔记了&#xff0c;制作以下图文笔记&#xff0c;仅供参考…… 鼓组加花 鼓的变动 进鼓后然后就可以动次打次了 下面是2个底鼓的加花

基于投影滤波算法的rick合成地震波滤波matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 RICK合成地震波模型 4.2 投影滤波算法原理 5.完整工程文件 1.课题概述 基于投影滤波算法的rick合成地震波滤波matlab仿真。分别通过标准的滤波投影滤波以及卷积滤波投影滤波对合成地震剖面进行滤波…

SQL第10课挑战题

1. 从OrderItems表中返回每个订单号order_num各有多少行数order_lines&#xff0c;并按order_lines对结果进行排序 2. 返回名为cheapest_item的字段&#xff0c;该字段包含每个供应商成本最低的产品&#xff08;使用products表中的prod_price)&#xff0c;然后从最低成本到最高…

Redis篇(缓存机制 - 基本介绍)(持续更新迭代)

目录 一、缓存介绍 二、经典三缓存问题 1. 缓存穿透 1.1. 简介 1.2. 解决方案 1.3. 总结 2. 缓存雪崩 2.1. 简介 2.2. 解决方案 2.3. 总结 3. 缓存击穿 3.1. 简介 3.2. 解决方案 3.3. 总结 4. 经典三缓存问题出现的根本原因 三、常见双缓存方案 1. 缓存预热 1…

MQ高级:RabbitMQ小细节

在之前的学习中&#xff0c;我们只介绍了消息的发送&#xff0c;但是没有考虑到异常的情况&#xff0c;今天我们就介绍一些异常情况&#xff0c;和细节的部分。 目录 生产者可靠性 生产者重连 生产者确认 MQ可靠性 持久化 Lazy Queue 消费者可靠性 消费者确认机制 失…