二叉搜索树介绍以及实现

        二叉树无论是在实际运用还是面试题中,都是一种十分热门的数据结构,而二叉搜索树则是进阶版的二叉树,在map和set中也有应用。

什么是二叉搜索树

二叉搜索树又叫二叉排序树,它可以是一颗空树,又或者是有以下三个特点的树。

  • 若它的左子树不为空,则左子树的所有节点的值都小于根节点的值。
  • 若它的右子树不为空,则右子树的所有节点的值都大于根节点的值。
  • 它的左右子树也都是二叉搜索树。

因为二叉搜索树具有以上三个特性,因此二叉搜索树的最优搜索次数为 O(log^2) ,最差搜索次数为 O(N)。

此外,中序遍历一个二叉搜索树所得到的结果应该是一个有序的数组

二叉搜索树的实现

二叉搜索树的查找

  • 从根节点开始查找,若查找的 val 大于根节点的值,则向右子树查找,否则向左子树查找
  • 最多查找高度次,走到空还没有找到,就返回nullptr,否则就返回这个节点。
pNode find(const T& val)
{
	pNode cur = Root;
	if (cur->_data == val)
	{
		return cur;
	}
	while (cur != nullptr)
	{
		if (val > cur->_data)
		{
			cur = cur->_right;
		}
		else if (val < cur->_data)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

二叉搜索树的插入

 二叉树的插入需要找到正确的位置。

  • val大于当前节点的值就向右子树走
  • val小于当前节点的值就向左子树走
  • 直到找到一个空节点就插入
bool insert(T val)
{
	pNode cur = new Node(val);
	pNode root = Root;
	if (Root!=nullptr&&find(val) != nullptr)
	{
		return false;
	}
	if (root == nullptr)
	{
		Root = cur;
		return true;
	}

	while (root != nullptr)
	{
		if (val > root->_data)
		{
			if (root->_right == nullptr)
			{
				root->_right = cur;
				break;
			}
			root = root->_right;
		}
		else if (val < root->_data)
		{
			if (root->_left == nullptr)
			{
				root->_left = cur;
				break;
			}
			root = root->_left;
		}
	
	}
	return true;
}

二叉搜索树的删除

二叉搜索树的删除是最难的,需要分情况说明。

  • 左子树为空,但右子树不为空
  • 右子树为空,但左子树不为空
  • 左右都为空
  • 左右都不为空

对于最后一种情况来说,这个节点是一个叶子节点,可以直接删除

右为空左不为空

当 cur 节点是父节点的左子树,就需要将父节点的左向量连接在cur节点的左子树上。

        

反之当cur节点是父节点的右子树,就需要将父节点的右向量连接在cur节点的左子树上。

左为空右不为空

若cur节点的左子树为空,且cur节点是父节点的左子树,就需要将父节点的左向量连接在cur节点的右子树上。

cur节点是父节点的右子树,则需要将父节点的右向量连接在cur节点的右子树上。

左右不为空 

若节点的左右子树都不是空树,为了不破坏二叉搜索树的结构,就需要利用一个中间变量(minright)来辅助删除。

minright应是被删节点的右子树中的最左节点。

1:若minright是被删节点的右节点。

这种情况下就直接交换cur和minright 的值,然后让cur 的右指针指向 minright 的右节点。

2:minright不是被删节点的右节点。

 

这种情况下,就需要记录 minright 的 parent 节点,让 parent 的左指针指向 minright 的右节点。

然后再交换 cur 和 minright 的值,再删除 minright 的节点。

	bool erase(T val)
	{
		pNode cur = Root;
		pNode parent = nullptr;
		while (cur != nullptr)
		{
			//大于向右走
			if (val > cur->_data)
			{
				parent = cur;
				cur = cur->_right;
			}
			//小于向左走
			else if (val < cur->_data)
			{
				parent = cur;
				cur = cur->_left;
			}
			//等于则开始消除
			else
			{
				if (cur->_right == nullptr)
				{
					//如果是根节点,就直接拿左节点当根
					if (cur == Root)
					{
						Root = cur->_left;

					}
					//否则就正常进行
					else
					{
						//如果cur是parent的右节点,就直接拿cur的左节点和parent的右节点相连
						if (cur == parent->_right)
						{
							parent->_right = cur->_left;
						}
						//如果cur是parent的左节点,就拿cur的左节点和parent的左节点相连
						else if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
					}
					delete cur;
				}
				else if (cur->_left == nullptr)
				{
					//如果是根节点
					if (cur == Root)
					{
						Root = cur->_right;
					}
					//否则就正常进行
					else
					{
						//如果cur是parent的右节点,就直接拿cur的左节点和parent的右节点相连
						if (cur == parent->_right)
						{
							parent->_right = cur->_right;
						}
						//如果cur是parent的左节点,就拿cur的左节点和parent的左节点相连
						else if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
					}
					delete cur;
				}
				//左右都不为空
				else
				{
					pNode minright = cur->_right;
					parent = cur;
					while (minright->_left)
					{
						parent = minright;
						minright = minright->_left;
					}
					cur->_data = minright->_data;
					if (minright == parent->_right)
					{
						parent->_right = minright->_right;
					}
					else
					{
						parent->_left = minright->_right;
					}
					delete minright;
				}
				return true;
			}
			
		}
		return false;
	}

 

完整代码

using namespace std;
template<class T>
class BSTNode {
public:
	BSTNode(T data)
		:_left(nullptr),_right(nullptr),_data(data)
	{
	}
	T _data;
	BSTNode<T>* _left;
	BSTNode<T>* _right;
};

template<class T>
class BSTree {
	typedef BSTNode<T> Node;
	typedef Node* pNode;
public:
	BSTree()
		:Root(nullptr)
	{}

	~BSTree()
	{
		destroy(Root);
	}
	void destroy(pNode root)
	{
		if (root->_left != nullptr)
		{
			destroy(root->_left);
		}
		if (root->_right != nullptr)
		{
			destroy(root->_right);
		}
		delete(root);
	}

	pNode find(const T& val)
	{
		pNode cur = Root;
		if (cur->_data == val)
		{
			return cur;
		}
		while (cur != nullptr)
		{
			if (val > cur->_data)
			{
				cur = cur->_right;
			}
			else if (val < cur->_data)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}
	bool erase(T val)
	{
		pNode cur = Root;
		pNode parent = nullptr;
		while (cur != nullptr)
		{
			//大于向右走
			if (val > cur->_data)
			{
				parent = cur;
				cur = cur->_right;
			}
			//小于向左走
			else if (val < cur->_data)
			{
				parent = cur;
				cur = cur->_left;
			}
			//等于则开始消除
			else
			{
				if (cur->_right == nullptr)
				{
					//如果是根节点,就直接拿左节点当根
					if (cur == Root)
					{
						Root = cur->_left;

					}
					//否则就正常进行
					else
					{
						//如果cur是parent的右节点,就直接拿cur的左节点和parent的右节点相连
						if (cur == parent->_right)
						{
							parent->_right = cur->_left;
						}
						//如果cur是parent的左节点,就拿cur的左节点和parent的左节点相连
						else if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
					}
					delete cur;
				}
				else if (cur->_left == nullptr)
				{
					//如果是根节点
					if (cur == Root)
					{
						Root = cur->_right;
					}
					//否则就正常进行
					else
					{
						//如果cur是parent的右节点,就直接拿cur的左节点和parent的右节点相连
						if (cur == parent->_right)
						{
							parent->_right = cur->_right;
						}
						//如果cur是parent的左节点,就拿cur的左节点和parent的左节点相连
						else if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
					}
					delete cur;
				}
				//左右都不为空
				else
				{
					pNode minright = cur->_right;
					parent = cur;
					while (minright->_left)
					{
						parent = minright;
						minright = minright->_left;
					}
					cur->_data = minright->_data;
					if (minright == parent->_right)
					{
						parent->_right = minright->_right;
					}
					else
					{
						parent->_left = minright->_right;
					}
					delete minright;
				}
				return true;
			}
			
		}
		return false;
	}
	bool insert(T val)
	{
		pNode cur = new Node(val);
		pNode root = Root;
		if (Root!=nullptr&&find(val) != nullptr)
		{
			return false;
		}
		if (root == nullptr)
		{
			Root = cur;
			return true;
		}

		while (root != nullptr)
		{
			if (val > root->_data)
			{
				if (root->_right == nullptr)
				{
					root->_right = cur;
					break;
				}
				root = root->_right;
			}
			else if (val < root->_data)
			{
				if (root->_left == nullptr)
				{
					root->_left = cur;
					break;
				}
				root = root->_left;
			}
		
		}
		return true;
	}
private:
	pNode Root;
};

总结

二叉搜索树是一种进阶版的二叉树结构,实际应用中十分广泛,面试题中经常出现,而在二叉搜索树之上还有AVL树和红黑树,不过这些都是后话了。

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

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

相关文章

用简单的方式理解串行主从通信方式(适用于LEU与TCC等外部设备之间的通信)

串行通信方式 数据按位序传送&#xff0c;最少需要两根通道&#xff08;如RS485、CAN总线、以太网&#xff09;。 优点&#xff1a;成本低&#xff0c;适合远距离通信。 缺点&#xff1a;速度慢、效率低。 注&#xff1a;以上特征为相较于并行通信方式。 主从通信方式 以RS4…

skimage图像处理(全)

文章目录 一、简介二、安装三、模块简介&#xff1a;API reference四、项目实战4.1、2D图像处理4.1.1、打印图像属性4.1.2、读取 / 显示 / 保存图像&#xff1a;skimage.io.imread() skimage.io.imshow() skimage.io.imsave()4.1.3、颜色空间转换&#xff1a;skimage.color.r…

轻松搞定软件开发:找对软件开发公司的流程与注意事项!

随着数字化时代的来临&#xff0c;软件开发在企业和个人生活中扮演着越来越重要的角色&#xff0c;然而&#xff0c;如何找到一家合适的软件开发公司却成为了一个令人头疼的问题。 本文将为你详细解读找软件开发公司的流程&#xff0c;以及在选择过程中需要注意的事项&#xf…

十大排序的个人总结之——冒泡排序、插入排序

同样&#xff0c;这两几乎也是被淘汰了的算法&#xff0c;尽管它们是稳定的&#xff0c;但是时间复杂度没人喜欢&#xff0c;了解一下就好&#xff0c;没啥好说的&#xff0c;注意最后一句话就行了 一&#xff0c;冒泡排序 1. 算法步骤 共n-1趟&#xff0c;谁两敢冒泡就换了…

C++ 不能用作全局变量名或给定 C 语言的链接

错误&#xff1a; C 不能用作全局变量名或给定 C 语言的链接 解决方案&#xff1a; 先抽自己两巴掌。 问问自己main函数作为一个函数&#xff0c;后面有没有添加&#xff08;&#xff09;&#xff1f; 如果没有&#xff0c;建议再给自己两巴掌。

解决Golang WriteHeader设置后,Content-Type失效的问题

场景 最近笔者在研究web框架过程中&#xff0c;发现了一个响应类型的问题&#xff0c;困扰许久&#xff0c;原因就是设置了响应状态码后&#xff0c;然后设置响应类型为application/json。在实际请求后&#xff0c;响应类型变成了text/plain; charsetutf-8格式。 问题解决&…

梳理Langchain-Chatchat-UI接口文档

在 Langchain-Chatchat v0.1.17 版本及以前是有前后端分离的 Vue 项目的&#xff0c;但是 v0.2.0 后就没有了。所以本文使用的是 Langchain-Chatchat v0.1.17 版本中的 Vue 项目。经过一番折腾终于将 Langchain-Chatchat v0.1.17 版本前端 Vue 接口和 Langchain-Chatchat v0.2.…

YOLOv8改进 | 注意力篇 | ACmix自注意力与卷积混合模型(提高FPS+检测效率)

一、本文介绍 本文给大家带来的改进机制是ACmix自注意力机制的改进版本&#xff0c;它的核心思想是&#xff0c;传统卷积操作和自注意力模块的大部分计算都可以通过1x1的卷积来实现。ACmix首先使用1x1卷积对输入特征图进行投影&#xff0c;生成一组中间特征&#xff0c;然后根…

vim学习记录

目录 历史记录前言相关资料配置windows互换ESC和Caps Lock按键 基本操作替换字符串 历史记录 2024年1月2日, 搭建好框架,开始学习; 前言 vim使用很久了,但是都是一些基本用法,主要是用于配置Linux,进行一些简单的编写文档和程序.没有进行过大型程序开发,没有达到熟练使用的程…

基础算法(8):高精度加减乘除

目录 1.高精度加法 模板&#xff1a; 例题&#xff1a; 2.高精度减法 模板&#xff1a; 例题&#xff1a; 3.高精度乘法 3.1 高精度乘低精度 模板&#xff1a; 例题&#xff1a; 3.2 高精度乘高精度 模板&#xff1a; 例题&#xff1a; ​编辑 4.高精度除法 模…

2024年山东省某市建筑类中级职称评审安利

2024年山东省某市建筑类中级职称评审安利 之所以安利山东烧烤市建筑类中级职称是因为2023年申报评审情况比较乐观。如果你2023年申报评审中级职称不顺利&#xff0c;等1-2年还没下来&#xff0c;可以考虑考虑山东省中级职称申报评审。叙后尘来给你们展开说说山东中级职称申报。…

科普帖:什么是函数即服务 (FaaS)?

您可能听说过SaaS&#xff0c;您可能听说过PaaS和IaaS&#xff0c;但您听说过函数即服务 (FaaS) 吗&#xff1f; FaaS市场正在快速增长。根据Allied Market Research的数据&#xff0c;2018年市场价值30.1亿美元 。预计到2026年&#xff0c;这一数字将增长到240亿美元——这意…

gitee创建仓库

描述 本文章记录了怎么在gitee上创建项目&#xff0c;以及使用vscode提代码到远程呢个仓库&#xff0c;如何创建一个新分支&#xff0c;并将新分支提交到远程仓库。 1、创建远程仓库 在创建远程仓库之前要先进行ssh密钥的设置 &#xff08;1&#xff09;打开黑窗口&#xff…

SpringBoot整合多数据源,并支持动态新增与切换

SpringBoot整合多数据源&#xff0c;并支持动态新增与切换 一、概述 在项目的开发过程中&#xff0c;遇到了需要从数据库中动态查询新的数据源信息并切换到该数据源做相应的查询操作&#xff0c;这样就产生了动态切换数据源的场景。为了能够灵活地指定具体的数据库&#xff0…

图解Kafka Producer常用性能优化配置参数

1 基本参数 bootstrap.servers&#xff1a;Kafka broker服务器地址列表&#xff0c;,分开&#xff0c;可不必写全&#xff0c;Kafka内部有自动感知Kafka broker的机制 client.dns.lookup&#xff1a;客户端寻找bootstrap地址的方式&#xff0c;支持两种方式&#xff1a; resol…

现在学鸿蒙开发有前途吗?能找到工作吗?

鸿蒙开发前景肯定是有的&#xff0c;我们可以从市场的情况来分析。 1、鸿蒙开发不兼容安卓 23年9月举办的华为新品发布会中&#xff0c;华为方面宣布开始启用原生鸿蒙应用&#xff0c;并不再提供安卓代码的兼容性。涵盖了资讯、社交、工具、金融、生活、美食、游戏等多品类的…

多技术融合在生态系统服务功能社会价值评估中的应用及论文写作、拓展分析

生态系统服务是人类从自然界中获得的直接或间接惠益&#xff0c;可分为供给服务、文化服务、调节服务和支持服务4类&#xff0c;对提升人类福祉具有重大意义&#xff0c;且被视为连接社会与生态系统的桥梁。自从启动千年生态系统评估项目&#xff08;Millennium Ecosystem Asse…

【Bootstrap学习 day8】

加载器 使用Bootstrap读取图标以表示元件加载状态&#xff0c;这些读取图标完全使用HTML,CSS。要创建spinner/加载器&#xff0c;可以使用.spinner-border类 <div class"spinner-border"></div>可以使用文本颜色类设置不同的颜色&#xff1a; <div …

关于Github部分下载的方法

一、问题 在Github中&#xff0c;我需要下载部分文件&#xff0c;而github只有下载最原始文件夹和单独文件的功能。 比如我想下载头四个文件&#xff0c;难以操作。 二、方法 推荐使用谷歌浏览器&#xff0c;进入扩展程序界面&#xff1a; 在应用商店获取GitZip for github…

Python数据科学应用从入门到精通--Python读取、合并SPSS数据文件

在很多情况下&#xff0c;我们需要调用SPSS软件产生的数据&#xff0c;下面通过示例来进行讲解。首先需要将本书提供的数据文件存储在安装spyder-py3的默认路径位置&#xff08;C:/Users/Administrator/.spyder-py3/&#xff0c;注意具体的安装路径可能与此不同&#xff09;&am…