数据结构——二叉搜索树详解

news2024/9/29 11:35:22

一、二叉搜索树定义

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

1.非空左子树上所有节点的值都小于根节点的值

2.非空右子树上所有节点的值都大于根节点的值

3.左右子树也都为二叉搜索树。

如下图所示:

二、二叉搜索树的操作

二叉搜索树结构:

template<class k>
	struct BSTreeNode {
		typedef BSTreeNode<k> Node;
		Node* _left;//左子树指针
		Node* _right;//右子树指针
		k _key;//节点数据
	};

2.1 二叉搜索树的查找

1.从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

2.最多查找高度次,走到到空,还没找到,这个值不存在。

非递归查找:

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

递归查找:

bool _FindR(Node* root, const k& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else
			{
				return true;
			}
		}

2.2 二叉搜索树的插入

插入的具体过程如下:

1. 树为空,则直接新增节点,赋值给root指针。

2. 树不空,按二叉搜索树性质查找插入位置,插入新节点。

插入key值为9的节点,如下图所示:

非递归插入:

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;//key值已存在
				}
			}
			cur = new Node(key);//以key值开辟节点
			if (parent->_key < key) {//新节点>父亲节点,在父亲的右边
				parent->_right = cur;
			}
			if (parent->_key > key) {//新节点<父亲节点,在父亲的左边
				parent->_left = cur;
			}
			return true;
		}

递归插入:

bool _InsertR(Node*& root, const k& key)
		{//递归查找传参指针的引用,修改原指针,让原指针直接指向当前节点
			if (root == nullptr)//root节点为空,开辟新结点
			{
				root = new Node(key);
				return true;
			}

			if (root->_key < key)//新节点>父亲节点,递归右树
			{
				return _InsertR(root->_right, key);
			}
			else if (root->_key > key)//新节点<父亲节点,递归左树
			{
				return _InsertR(root->_left, key);
			}
			else
			{
				return false;
			}
		}

2.3 二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情 况:

1. 要删除的结点无孩子结点 2. 要删除的结点只有左孩子结点 3. 要删除的结点只有右孩子结点 4.要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程 如下:

1.删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除。

2.删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除。 

3.查找删除结点的右子树的最左节点或者左子树的最右节点(距离删除节点最近,替换后不影响其他节点位置),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除。下面用右子树的最左节点进行替换。

非递归删除:

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 (cur == parent->_right)
							{//判断删除节点是parent的left还是right
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}
						delete cur;
						return true;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_right)
							{//判断删除节点是parent的left还是right
								parent->_right = cur->_left;
							}
							else
							{
								parent->_left = cur->_left;
							}
						}
						delete cur;
						return true;
					}
					else 
					{
			        //左右子树均有节点,将删除节点替换为其右子树的最左节点(左子树的最右节点)
						Node* rightMinParent = cur;//考虑右子树的根节点为最左节点,不进循环
						Node* rightMin = cur->_right;
						while (rightMin->_left) 
						{
							rightMinParent = rightMin;
							rightMin = rightMin->_left;
						}
						cur->_key = rightMin->_key;
						if (rightMin == rightMinParent->_left) 
						{
							rightMinParent->_left = rightMin->_right;
                   //rightMin(右子树最左)为替换节点,替换后删除,rightMinParent的左或右指向rightMin的right
						}
						else 
						{
							rightMinParent->_right = rightMin->_right;
						}
						delete rightMin;
						return true;
					}
				}
			}
			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->_right == nullptr)
				{//删除节点右为空,将左节点赋值给要删除的节点
					root = root->_left;
				}
				else if (root->_left == nullptr)
				{//删除节点左为空,将右节点赋值给要删除的节点
					root = root->_right;
				}
				else
				{
					Node* rightMin = root->_right;
					while (rightMin->_left)
					{
						rightMin = rightMin->_left;
					}
					
					swap(root->_key, rightMin->_key);//交换删除节点和右子树节点的key值
					return _EraseR(root->_right, key);//交换后,删除节点为右子树最左节点,走left为空时情况
				}

				delete del;
				return true;
			}

		}

三、二叉搜索树的模拟实现

//二叉搜索树的模拟实现
namespace rab {
	template<class k>
	struct BSTreeNode {
		typedef BSTreeNode<k> Node;
		Node* _left;
		Node* _right;
		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() {
			Destroy(_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;//key值已存在
				}
			}
			cur = new Node(key);
			if (parent->_key < key) {
				parent->_right = cur;
			}
			if (parent->_key > key) {
				parent->_left = cur;
			}
			return true;
		}

		//查找
		bool 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 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 (cur == parent->_right)
							{//判断删除节点是parent的left还是right
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}
						delete cur;
						return true;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_right)
							{//判断删除节点是parent的left还是right
								parent->_right = cur->_left;
							}
							else
							{
								parent->_left = cur->_left;
							}
						}
						delete cur;
						return true;
					}
					else 
					{
						//左右子树均有节点,将删除节点替换为其右子树的最左节点(左子树的最右节点)
						Node* rightMinParent = cur;//考虑右子树的根节点为最左节点,不进循环
						Node* rightMin = cur->_right;
						while (rightMin->_left) 
						{
							rightMinParent = rightMin;
							rightMin = rightMin->_left;
						}
						cur->_key = rightMin->_key;
						if (rightMin == rightMinParent->_left) 
						{
							rightMinParent->_left = rightMin->_right;//rightMin(右子树最左)为替换节点,替换后删除,rightMinParent的左或右指向rightMin的right
						}
						else 
						{
							rightMinParent->_right = rightMin->_right;
						}
						delete rightMin;
						return true;
					}
				}
			}
			return false;
		}
		
		/二叉搜索树的递归实现
		//递归查找
		bool FindR(const k& key)
		{
			return _FindR(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;
		}


	private:
		//递归中序遍历
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;

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

		//递归销毁搜索二叉树
		void Destroy(Node* root) {
			if (root == nullptr) {
				return;
			}
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
		}

		//前序拷贝构造
		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;
		}
		
		//递归查找
		bool _FindR(Node* root, const k& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else
			{
				return true;
			}
		}

		//递归插入
		bool _InsertR(Node*& root, const k& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				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->_right == nullptr)
				{
					root = root->_left;
				}
				else if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else
				{
					Node* rightMin = root->_right;
					while (rightMin->_left)
					{
						rightMin = rightMin->_left;
					}
					
					swap(root->_key, rightMin->_key);
					return _EraseR(root->_right, key);//交换后,删除节点为右子树最左节点,走left为空时情况
				}

				delete del;
				return true;
			}

		}


	private:
		Node* _root = nullptr;
		};
	}

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

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

相关文章

SinoDB备份恢复工具之ontape

ontape是SinoDB数据库的备份恢复工具之一&#xff0c;可以记录、备份与恢复数据。因为不涉及到存储管理器&#xff0c;所以可以轻松实现其他备份方法&#xff0c;比如备份到 STDIO&#xff08;标准输出&#xff09;或备份到云。 1. ontape功能特性 备份数据库服务器实例支持增…

【蓝桥杯省赛真题34】python积木搭建 中小学青少年组蓝桥杯比赛 算法思维python编程省赛真题解析

python积木搭建 第十三届蓝桥杯青少年组python比赛省赛真题 一、题目要求 &#xff08;注&#xff1a;input&#xff08;&#xff09;输入函数的括号中不允许添加任何信息&#xff09; 1、编程实现 小蓝和小青在玩积木搭建游戏&#xff0c;具体玩法如下: 小蓝报一个数字N&…

展示大屏-24小时天气预报

一、项目说明 展示大屏显示未来一周天气和24小时天气详情。 二、技术工具 1.语言&框架&#xff1a;java、springboot 2.UI界面&#xff1a;jQuery、HTML、CSS、 VUE 3.开发工具&#xff1a;IntelliJ IDEA、Eclipse 三、实现步骤 后端步骤 1.调取免费或收费的API接口。 …

【一】TensorFlow神经网络模型构建之神经元函数及优化方法

TensorFlow神经网络模型构建主要涉及如下几块&#xff1a;神经元函数、卷积函数、池化函数、分类函数、优化方法。下面分别对这几块进行展开说明&#xff1a; 神经元函数及优化方法 神经网络之所以能解决非线性问题&#xff08;如语音、图像识别等&#xff09;&#xff0c;本…

共建数治政务,共赢数智未来——锐捷网络召开2024政府行业全国核心合作伙伴大会

在我国不断推进现代化产业体系建设,新质生产力正进入跃迁式发展的今天,加速政府的数字化转型,促进数字技术与政府应用深度融合,也正成为越来越多地方政府的共识。为了更好地携手合作伙伴共同把握数智政务建设新契机、聚力提升数智政务建设新能力,3月22日,以“共建数治政务,共赢…

C++第十三弹---内存管理(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、operator new与operator delete函数 1.1、operator new与operator delete函数 2、new和delete的实现原理 2.1、内置类型 2.2、自定义类型 …

星光/宝骏/缤果/长安 车机CarPlay手机操作破解教程V2.0版本(无需笔记本、无需笔记本、无需笔记本)

之前写了个1.0版本&#xff0c;由于太局限&#xff0c;需要用到笔记本才能操作&#xff0c;很多车友反馈不方便。特此出个手机版教程&#xff0c;简单easy&#xff0c;妈妈再也不用担心我搞不定啦 一、准备工作 先卸载车机上的autokit 或者 智能互联 app&#xff0c;这步很关…

使用Qt生成图片

Qt之生成png/jpg/bmp格式图片_qt生成图片-CSDN博客 (1)使用QPainter 示例关键代码&#xff1a; QImage image(QSize(this->width(),this->height()),QImage::Format_ARGB32);image.fill("white");QPainter *painter new QPainter(&image);painter->…

详解 WebWorker 的概念、使用场景、示例

前言 提到 WebWorker&#xff0c;可能有些小伙伴比较陌生&#xff0c;不知道是做什么的&#xff0c;甚至不知道使用场景&#xff0c;今天这篇文章就带大家一起简单了解一下什么是 webworker&#xff01; 概念 WebWorker 实际上是运行在浏览器后台的一个单独的线程&#xff0…

[PCL] PCLVisualizer可视化的应用

[PCL] PCLVisualizer可视化的应用 通常情况下 PCL的可视化 是非常方便. 但是 #include<pcl/io/io.h> #include<pcl/io/pcd_io.h> #include<pcl/io/ply_io.h> #include<pcl/visualization/cloud_viewer.h>#include<iostream> #include<threa…

【函数修改的重要问题】想要增加C++函数返回值,选择结构体?OR 额外参数?

作为一个程序员&#xff0c;我们经常会遇到这样的情况&#xff1a; 别人写的C项目&#xff0c;需要我们来进行 ”修改&#xff0c;或者增加功能“。 举个例子 我们需要对于一个已有的C函数&#xff0c;增加它的返回值信息&#xff0c;通常有两条路可走&#xff1a; 用结构体…

只用一部手机,为你的吉利车机安装第三方软件(支持大部分车型)

&#x1f4a1; 文中使用的方法为DNS重定向&#xff0c;需要你具备一定的动手能力&#xff0c;否则将无法达到最终目的。 &#x1f4a1; 阅读本文&#xff0c;视为你有一定电脑基础&#xff0c;难以理解时&#xff0c;请寻求百度帮助&#xff0c;百度就是最好的老师&#xff01;…

“大模型+机器人”的现状和未来,重磅综述报告来了!

基础模型(Foundation Models)是近年来人工智能领域的重要突破&#xff0c;在自然语言处理和计算机视觉等领域取得了显著成果。将基础模型引入机器人学&#xff0c;有望从感知、决策和控制等方面提升机器人系统的性能&#xff0c;推动机器人学的发展。由斯坦福大学、普林斯顿大学…

红队笔记8-CTF5打靶流程-CMS漏洞-多用户信息泄露(vulnhub)

目录 开头: 1.主机发现和端口扫描&#xff1a; 2.80端口-NanoCMS哈希密码信息泄露-后台getshell 3.提权-用户过多信息泄露 4.总结&#xff1a; 开头: 学习的视频是哔哩哔哩红队笔记&#xff1a; 「红队笔记」靶机精讲&#xff1a;LAMPSecurityCTF5 - 标准攻击链&#xff…

【Java程序设计】【C00392】基于(JavaWeb)Springboot的校园生活服务平台(有论文)

基于&#xff08;JavaWeb&#xff09;Springboot的校园生活服务平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过…

有趣的css - 多弧形加载动画

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是用纯css实现多双弧线加载动画。 《有趣的css》系列最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面css 样式页面渲…

MySQL数据库基础--约束

约束 约束是作用于表中字段上的规则&#xff0c;用于限制在表中的数据 目的&#xff1a;保证数据库中数据的正确&#xff0c;有效性和完成性。 分类&#xff1a; 注意&#xff1a;约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的时候哦添加约束 外键约束 外键用…

心理治疗聊天机器人的调查

摘要 这项调查旨在调查、分析和比较现有聊天机器人在心理治疗中的可行性和缺陷。调查指出了未来心理治疗聊天机器人所需的一系列任务。我们在公共数据库中检索了约1200篇相关文献&#xff0c;并选择了五种典型的和最先进的心理治疗聊天机器人。大多数最先进的心理治疗聊天机器人…

AndroidStudio中一些实用插件

1.RainbowBrackets插件为圆括号、方括号和花括号内的代码添加了漂亮的彩虹色 2.CodeGlance类似于Sublime或Xcode&#xff0c;CodeGlance插件在编辑器中嵌入了代码迷你图。滚动条也有所增大。在CodeGlance预览文件的代码模式下&#xff0c;用户可以快速导航到目标处。 3.ADBWifi…