C++搜索二叉树

news2025/1/18 9:07:54

一、搜索二叉树概念

搜索二叉树是一种树形结构,常用于map当中。搜索二叉树严格遵守左小右大的规则

C语言中实现搜索二叉树有一些困难,并且在面对一些特定题目实现较困难。因此采用C++的方式再次实现搜索二叉树

二、搜索二叉树的实现

插入

 搜索二叉树在插入之前必须要先遍历查找到合适的位置,再找到合适的位置后将结点插入。这里需要考虑如何将插入节点与原二叉树连接,因此需要同时记录父节点以方便连接

	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->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;
	}

删除

删除需要考虑的情况比插入更复杂:

①在叶子节点删除

下图中4、6、15都是叶子节点,删除叶子节点的处理比较简单,直接删除即可

②删除有左孩子或者有右孩子的节点

寻找删除节点的父节点,继承删除节点的子节点

这里需要考虑删除根节点的问题,如果根节点只有单一子树,由于根节点并没有父节点,会出现空指针问题

因此如果删除节点为根节点,并且根节点只有单一子树,则更新根节点(采用替代法也可以,但是更新根节点更简单)

③删除同时有左孩子和右孩子的节点

只能间接删除,同时有两个孩子,就只能采用替代法。选择删除节点左子树上的最大节点或者右子树上的最小节点替代该节点,因为需要保持左小右大的规则

	bool Erase(const K& key)
	{
		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
			{
				if(cur->_left==nullptr)//单个孩子左为空
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							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
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else//同时有两个孩子
				{
					//找要删除节点的右树最小节点或左树最大节点替代
					Node* minRight = cur->_right;//右树最小
					Node* pminRight = cur;//minRight的父节点
					//这里不赋值为nullptr也是考虑到如果删除根节点的空指针问题
					while (minRight->_left)//最小需要往左找
					{
						pminRight = minRight;
						minRight = minRight->_left;
					}
					cur->_key = minRight->_key;//替代
					//pminRight->_left = minRight->_right;对于根节点会有空指针问题
					if (pminRight->_left == minRight)//考虑根节点删除问题
					{
						pminRight->_left = minRight->_right;
					}
					else
					{
						pminRight->_right = minRight->_right;
					}	
					delete minRight;
				}
				return true;
			}
		}
		return false;
	}

整体实现

#pragma once
#include<iostream>
using namespace std;


template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _right;
	BSTreeNode<K>* _left;
	K _key;

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

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:


	BSTree() = default;//强制生成默认构造
	BSTree(const BSTree<K>& t)//拷贝构造
	{
		_root = Copy(t._root);
	}
	BSTree<K>& operator=(BSTree<K> t)//赋值构造
	{
		swap(_root, t._root);
		return *this;
	}
	~BSTree()
	{
		Destory(_root);
	}

	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->_right = cur;
		}
		else
		{
			parent->_left = 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->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				if(cur->_left==nullptr)//单个孩子左为空
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							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
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else//同时有两个孩子
				{
					//找要删除节点的右树最小节点或左树最大节点替代
					Node* minRight = cur->_right;//右树最小
					Node* pminRight = cur;//minRight的父节点
					//这里不赋值为nullptr也是考虑到如果删除根节点的空指针问题
					while (minRight->_left)//最小需要往左找
					{
						pminRight = minRight;
						minRight = minRight->_left;
					}
					cur->_key = minRight->_key;//替代
					//pminRight->_left = minRight->_right;对于根节点会有空指针问题
					if (pminRight->_left == minRight)//考虑根节点删除问题
					{
						pminRight->_left = minRight->_right;
					}
					else
					{
						pminRight->_right = minRight->_right;
					}	
					delete minRight;
				}
				return true;
			}
		}
		return false;
	}

	//递归实现封装接口
	bool FindR(const K& key)
	{
		return _FindR(_root, key);
	}
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
	void InOrder()//二次封装,方便调用
	{
		_InOrder(_root);
		cout << endl;
	}



protected://递归实现
	void Destory(Node*& root)
	{
		if (root == nullptr)
		{
			return;
		}
		Destory(root->_left);
		Destory(root->_right);
		delete root;
		root = nullptr;
	}
	Node* Copy(Node*& root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		Node* newroot = new Node(root->_key);
		newroot->_left = Copy(root->_left);
		newroot->_right = Copy(root->_right);
		return newroot;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
	bool _FindR(Node* root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key = key)
		{
			return true;
		}
		if (root->_key < key)
		{
			return _FindR(root->_right, key);
		}
		else
			return _FindR(root->_left, key);
	}
	bool _InsertR(Node*& root, const K& key)
	//当走到空节点时,root是上一次调用的引用(root->_right或root->_left的引用)
	{
		if (root == nullptr)
		{
			root = new Node(key);
			//此时的root节点本就在树中,只不过为空。因此可以直接创建新节点而无需连接
			return true;
		}
		if (root->_key < key)
		{
			return _InsertR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _InsertR(root->_left, key);
		}
		else
		{
			return false;
		}
	}
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}

		if (root->_key < key)
		{
			return _EraseR(root->_right,key);
		}
		else if (root->_key > key)
		{
			return _EraseR(root->_left, key);
		}
		else
		{
			Node* del = root;
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				Node* maxleft = root->_left;
				while (maxleft->_right)
				{
					maxleft = maxleft->_right;
				}
				swap(root->_key , maxleft->_key);
				return _EraseR(_root->_left, key);
				//转换为子树删除,因为一定是右为空
				//这里不能是maxleft因为是引用传参
			}
			delete del;
			return true;
		}
	}
	
private:
	Node* _root = nullptr;
};

搜索二叉树的应用 

1.key模型

K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值

实际应用:车库比对车牌号<num>,人脸识别<face>

2.key value模型 

每一个关键码key,都有与之对应的值Value,即<key,value>的键值对  

实际应用:英汉词典中英相互对应<word,Chinese>、学生姓名与学号相互对应<name,num> 

#pragma once
//搜索二叉树的key,value模型
#include<iostream>
using namespace std;
template<class K, class V>
struct BSTreeNode
{
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;
	K _key;
	V _value;


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

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

	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->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			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->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				// 删除
				// 1、左为空
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}

					delete cur;

				} // 2、右为空
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}

					delete cur;
				}
				else
				{
					// 找右树最小节点替代,也可以是左树最大节点替代
					Node* pminRight = cur;
					Node* minRight = cur->_right;
					while (minRight->_left)
					{
						pminRight = minRight;
						minRight = minRight->_left;
					}

					cur->_key = minRight->_key;

					if (pminRight->_left == minRight)
					{
						pminRight->_left = minRight->_right;
					}
					else
					{
						pminRight->_right = minRight->_right;
					}

					delete minRight;
				}

				return true;
			}
		}

		return false;
	}


	void InOrder()
	{
		_InOrder(_root);
	}

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

		_InOrder(root->_left);
		cout << root->_key << ":" << root->_value<<" ";
		_InOrder(root->_right);
	}
private:
	Node* _root = nullptr;
};

搜索二叉树性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二 叉搜索树的深度的函数,即结点越深,则比较次数越多

最优情况最坏情况
树的形态搜索二叉树接近完全二叉树搜索二叉树为单边树
时间复杂度

由于搜索二叉树的形态不确定,导致其时间复杂度也取决于二叉树的形态(比如单边树)因此出现了平衡搜索二叉树,与搜索二叉树的不同就在于平衡。始终将二叉树调整到接近完全二叉树,使得时间复杂度为最优解

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

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

相关文章

ASEMI代理长电可控硅BT136参数,BT136规格,BT136说明

编辑-Z 长电可控硅BT136参数&#xff1a; 型号&#xff1a;BT136 RMS通态电流IT(RMS)&#xff1a;6A 非重复浪涌峰值导通电流ITSM&#xff1a;25A 峰值栅极电流IGM&#xff1a;2A 平均栅极功耗PG(AV)&#xff1a;0.5W 存储接点温度范围Tstg&#xff1a;-40 to 150℃ 工…

Linux -- 进阶 Web服务器 搭建基于 https 协议的静态网站 ( 预备知识 )

概念引入 &#xff1a; 什么是 HTTPS ? HTTPS 协议背景 &#xff1f; >>> HTTP 协议 即 超文本传输协议 &#xff0c; 它出生的很早&#xff0c;在早期&#xff0c;网络服务的相关攻击呢&#xff0c;或者病毒啥的几乎没有&#xff0c;安全类的问 题也是少之又…

【SpringCloud】Gateway网关

文章目录 1、网关的作用2、搭建网关服务3、路由断言4、GatewayFilter5、全局过滤器6、过滤器的执行顺序7、限流过滤器8、跨域问题处理 1、网关的作用 服务就像一个景点&#xff0c;如果人人可以访问&#xff0c;不管是游客还是搞破坏的人都放进来&#xff0c;那一定出事。由此…

零基础去学习渗透,很难吗?

前言&#xff1a; 很多朋友问我&#xff0c;想搞网络安全&#xff0c;编程重要吗&#xff0c;选什么语言呢&#xff1f; 国内其实正经开设网络安全专业的学校很少&#xff0c;大部分同学是来自计算机科学、网络工程、软件工程专业的&#xff0c;甚至很多非计算机专业自学的。…

抖音seo源码开发|矩阵号排名|账号矩阵系统开发搭建

源码展示 抖音seo又叫抖音搜索引擎&#xff0c;只要能做到布词&#xff0c;和过去的百度seo优化一样&#xff0c;布词&#xff0c;布关键词&#xff0c;当搜索栏搜索时可以搜索到该短视频。优化视频关键词&#xff0c;做好关键词的优化&#xff0c;就能在别人抖音搜索栏搜索的时…

C++数据结构:Python风格双向链表Pylist的实现

文章目录 前言一、目的二、代码详解1、Node类模板2、Pylist类构造3、内嵌迭代器4、Python风格insert方法5、Python风格append方法6、Python风格[]下标操作方法7、Python风格、 方法8、Python风格pop方法9、Python风格remove方法10、length、get方法 三、使用示例总结原创文章&a…

算法基础学习笔记——⑨C++STL使用技巧

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨CSTL简介 ✨CSTL使用技巧 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(∩_∩)O&#xff0c;感谢大家的支持&#xff01; ✨CSTL简介 vector变长数组&#xff0c;倍增的思想//系统为…

六级备考24天|CET-6|翻译技巧4-1|翻译红楼梦|22:40~23:40

目录 1 ANSWER 2 PRACTICE ANSWER ​ 3​ PRACTICE ANSWER 合并 ​ 全文翻译​ 1 ANSWER depict / dɪˈpɪkt / v.描述&#xff0c;描绘 第三人称单数 depicts 现在分词 depicting 过去式 depicted 过去分词 …

【C++】STL——反向迭代器的模拟实现:迭代器适配器

文章目录 前言1. list 的反向迭代器模拟实现2. 思考3. 库里面反向迭代器的实现——迭代器适配器4. 反向迭代器模拟实现的改进——适配器模式5. 适配器模式的实现——一劳永逸 前言 反向迭代器的使用相信大家都已经比较熟悉了&#xff0c;那我们这篇文章具体讲什么呢&#xff1f…

LAMP平台搭建

文章目录 LAMP概述安装apache安装mysql安装php LAMP概述 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整套系统和相关软件&#xff0c;能够提供动态Web站点服务及其应用开发环境。LAMP是一个缩写词&#xff0c;具体包括Linux操作系统、Apache网站…

HCIA-单点故障-Smart Link

目录 单点故障&#xff1a; 单设备”链路备份“方案 —— Smart Link Smart Link端口状态&#xff1a; Smart Link基础命令配置&#xff1a; 单设备”链路备份“方案 —— Smart Link案列实现 单设备”链路备份“方案 —— Monitor Link Monitor link组 Monitor link 的使用…

Zookeeper快速入门(Zookeeper概述、安装、集群安装、选举机制、命令行操作、节点类型、监听器原理)

1、Zookeeper入门 1.1 概述 Zookeeper是一个开源的分布式的&#xff0c;为分布式框架提供协调服务的Apache项目。 1、Zookeeper工作机制 Zookeeper从设置模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责储存和管理大家都关心的数…

spring源码篇(八)事务的原理

文章目录 前言基本操作验证 Spring事务的传播机制特殊的机制说明NOT_SUPPORTEDNESTEDSUPPORTS 源码加载事务自动配置类要不要加注解&#xff1a;EnableTransactionManagement配置类说明 EnableTransactionManagement 做了什么AutoProxyRegistrar做了什么创建的代理类是jdk动态代…

UE DTDataTable 插件说明, 运行中操作CSV文件。

UDataTable的扩展对象&#xff0c;可以在Runtime模式下可以加载和保存CSV文件&#xff0c;并且可以进行数据的添加和删除&#xff0c;也可以使用系统DataTable的所有函数。 1. Create DT Data Table From CSV File 从CSV文件创建 DT Data Table 对象 File Path &#xff1a;文件…

MiniConda、CUDA、CUDnn以及pytorch环境的配置以及坑

文章目录 一、MiniConda安装、介绍1.1 Conda是什么&#xff1f;1.2 MiniConda是什么&#xff1f;1.3 安装方法1.4 Conda常用命令1.5 杂项 二、CUDA 以及 CUDnn三、Pytorch安装总结 首先需要说明一下&#xff0c;我想安装的是Pytorch GPU版&#xff0c;所以需要安装CUDA toolkit…

WPF 页面布局 DockPanel Grid StackPanel UniformGrid WrapPanel WPF布局入门 WPF布局资料

在布局常用的布局属性 HorizontalAlignment: 用于设置元素的水平位置VerticalAlignment: 用于设置元素的垂直位置 Margin: 指定元素与容器的边距 Height: 指定元素的高度 Width: 指定素的宽度 WinHeight/WinWidth: 指定元素的最小高度和宽度MaxHeight/MaxWidth: 指定元素的最大…

Selenium自动化测试(基于Java)

目录 一. 了解Selenium ✅1.1 概念 ✅1.2 作用 ✅1.3 特点 ✅ 工作原理 二. Selenium Java 环境搭建 ✅2.1 下载 Chrome 浏览器 ✅2.2 查看浏览器的版本 ✅2.3 下载浏览器驱动 ✅2.4 验证环境是否搭建成功 三. Selenium 常用 API ✅3.1 定位元素 ✅3.2 操作对象 ✅…

给你的终端(windows 11的命令行 增加一个好看的背景)

1.win R 键入cmd 加回车 2.点击这个符号 进入设置 3. 找到命令提示符 4.往下滚动你的小鼠标 5.看见其他设置 点击外观 6.继续滚动小鼠标 找到背景图像 路径 最后选择 自己的喜欢图片的存放路径就可以啦 啦啦啦

宠物医院小程序开发需具备哪些功能?

想要开发一款专业好用的宠物医院小程序系统&#xff0c;需要具备哪些基本功能呢&#xff1f; 1、在线预约。用户可以在线上预约宠物医院的服务&#xff0c;包括医生、服务的具体内容、预约的时间、具体的宠物医院地址等&#xff0c;不仅可以帮助用户合理安排好时间还能让…

consul命令总结

1. consul members -http-addrxxxxx 查看指定地址consul集群的所有节点 举例&#xff1a;查看地址192.168.5.47上consul集群的所有consul节点 如下图&#xff0c;该集群一共有三个节点 2. consul info -http-addrxxxxx 查看指定地址consul集群的详细信息 举例&#xff1a;查看…