搜索二叉树(C++)

文章目录

  • 1. 搜索二叉树的概念
  • 2. 搜索二叉树结构的定义
  • 3. 搜索二叉树的操作
    • 3.1 搜索二叉树的插入
    • 3.2 搜索二叉树的删除
    • 3.3 搜索二叉树的查找
  • 4. 完整代码

1. 搜索二叉树的概念

二叉搜索树(Binary Search Tree,简称 BST),又称二叉排序树,是一种特殊的二叉树
二叉搜索树可以是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
  • 递归性质:它的左右子树也分别为二叉搜索树。
    在这里插入图片描述

2. 搜索二叉树结构的定义

搜索二叉树可以存储任何类型的数据,因此这里使用模板来实现搜索二叉树(模板允许我们创建一个泛型的节点结构,可以存储任意类型的值)。
一个二叉搜索树(BST)是一个节点的集合,这些节点具有如下结构:

  • 一个存储的值(_val)。
  • 指向左子节点的指针(_left,可以为空)。
  • 指向右子节点的指针(_right,可以为空)。
    在这里插入图片描述

节点具体定义如下:

template<class K>
struct TreeNode
{
	TreeNode<K>* _left;
	TreeNode<K>* _right;
	K _val;
	//构造函数
	TreeNode(const K& val)
		:_left(nullptr)
		,_right(nullptr)
		,_val(val)
	{}
};

搜索二叉树结构的定义:

template<class K>
class BSTree
{
public:
	typedef TreeNode<K> Node;
private:
	Node* _root = nullptr;//根节点指针 _root为空代表空树
};

3. 搜索二叉树的操作

3.1 搜索二叉树的插入

插入操作的具体过程

  1. 树为空:如果树为空,直接创建一个新节点,并将其赋值给根指针。
  2. 树不空:从根节点开始,比较待插入值与当前节点的值。
    如果待插入值小于当前节点的值,递归或迭代地在当前节点的左子树中查找插入位置。
    如果待插入值大于当前节点的值,递归或迭代地在当前节点的右子树中查找插入位置。
    在找到适当的空位置后,插入新节点。

非递归操作过程如下:
在这里插入图片描述

非递归代码如下:

bool Insert(const K& val)
{
	// 树为空,直接创建一个新节点,并将其赋值给根指针
	if (_root == nullptr)
	{
		_root = new Node(val);
		return true;
	}
	 //树不空,从根节点开始,比较待插入值与当前节点的值,直到cur走到空
	//找插入位置
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		parent = cur;
		if (cur->_val > val)
		{
			cur = cur->_left;
		}
		else if (cur->_val < val)
		{
			cur = cur->_right;
		}
		else
		{
			return false;
		}
	}
	//插入
	cur = new Node(val);
	if (val > parent->_val)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	return true;
}

递归操作过程如下:
在这里插入图片描述
递归代码如下:

bool _InsertR(Node*& root, const K& val)
{
	//1.树为空时,直接创建一个新节点,并将其赋值给root,这里的root是_root的引用
	//2.树为不空时,root其父节点的_right或者_left的引用
	if (root == nullptr)
	{
		root = new Node(val);
		return true;
	}
	//待插入值小于当前节点的值,递归去当前节点的左子树中查找插入位置
	if (root->_val > val)
	{
		return _InsertR(root->_left, val);
	}
	else if (root->_val < val)//待插入值大于当前节点的值,递归去在当前节点的右子树中查找插入位置
	{
		return _InsertR(root->_right, val);
	}
	else
	{
		return false;
	}
}
bool InsertR(const K& val)
{
	return _InsertR(_root, val);
}

3.2 搜索二叉树的删除

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

  • a. 要删除的结点无孩子结点
  • b. 要删除的结点只有左孩子结点
  • c. 要删除的结点只有右孩子结点
  • d. 要删除的结点有左、右孩子结点

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

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),或者在它的左子树中寻找中序下的最后一个结点(关键码最大),用它的值填补到被删除节点中,再来处理该结点的删除问题—替换法删除

画个图理解一下:
在这里插入图片描述
非递归代码如下:

bool Erase(const K& val)
{
	Node* parent = nullptr;//记录当前节点的父节点,初始时为空
	Node* cur = _root;//记录当前节点
	while (cur)
	{
		//待删除值大于当前节点的值,去当前节点的右子树去中查找删除
		if (val > cur->_val)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (val < cur->_val)//待删除值小于当前节点的值,去当前节点的左子树去中查找删除
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//准备删除
			if (cur->_left == nullptr)//左为空
			{
				//父节点为空,说明删除的是根节点
				if (parent == nullptr)
				{
					_root = cur->_right;
				}
				else
				{
					//将自己的右孩子托管给父节点
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if (cur->_right == nullptr)//右为空
			{
				//父节点为空,说明删除的是根节点
				if (parent == nullptr)
				{
					_root = cur->_left;
				}
				else
				{
					//将自己的左孩子托管给父节点
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			else
			{
				//找右子树最左节点
				Node* subLeft = cur->_right;
				Node* parent = cur;
				while (subLeft->_left)
				{
					parent = subLeft;
					subLeft = subLeft->_left;
				}
				swap(subLeft->_val, cur->_val);//替换
				//删除替换节点
				if (parent->_left == subLeft)
				{
					parent->_left = subLeft->_right;
				}
				else
				{
					parent->_right = subLeft->_right;
				}
				delete subLeft;
			}
			return true;
		}
	}
	return false;
}

递归代码如下:

bool _EraseR(Node*& root, const K& val)
{
	if (root == nullptr) return false;
	//待删除值小于当前节点的值,递归去当前节点的左子树去中查找删除
	if (root->_val > val)
	{
		return _EraseR(root->_left, val);
	}
	else if (root->_val < val)//待删除值大于当前节点的值,递归去当前节点的右子树去中查找删除
	{
		return _EraseR(root->_right, val);
	}
	else
	{
		//开始删除
		//左为空,将自己的右孩子托管给父节点
		if (root->_left == nullptr)
		{
			Node* del = root;
			root = root->_right;
			delete del;
		}
		else if (root->_right == nullptr)//右为空,将自己的左孩子托管给父节点
		{
			Node* del = root;
			root = root->_left;
			delete del;
		}
		else
		{
			//找左子树的最右节点替换删除
			Node* parent = root;
			Node* subRight = root->_left;
			while (subRight->_right)
			{
				parent = subRight;
				subRight = subRight->_right;
			}
			swap(subRight->_val, root->_val);
			_EraseR(root->_left, subRight->_val);
		}
		return true;
	}
}
bool EraseR(const K& val)
{
	return _EraseR(_root, val);
}

3.3 搜索二叉树的查找

具体操作步骤如下:

  1. 从根开始比较,查找,比根大去其右子树中查找,比根小则去其左子树中查找。
  2. 最多查找高度次,走到到空,还没找到,这个值不存在。
    在这里插入图片描述
    非递归代码如下:
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;
}

递归代码如下:

bool FindR(const K& val)
{
	return _FindR(_root, val);
}
bool _FindR(Node* root, const K& val)
{
	if (root == nullptr) return false;
	if (root->_val > val)
	{
		return _FindR(root->_left, val);
	}
	else if (root->_val < val)
	{
		return _FindR(root->_right, val);
	}
	else
	{
		return true;
	}
}

4. 完整代码

#include <iostream>
using namespace std;
template<class K>
struct TreeNode
{
	TreeNode<K>* _left;
	TreeNode<K>* _right;
	K _val;
	TreeNode(const K& val)
		:_left(nullptr)
		,_right(nullptr)
		,_val(val)
	{}

};
template<class K>
class BSTree
{
public:
	typedef TreeNode<K> Node;
	bool Insert(const K& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(val);
			return true;
		}
		//找插入位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if (cur->_val > val)
			{
				cur = cur->_left;
			}
			else if (cur->_val < val)
			{
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		//插入
		cur = new Node(val);
		if (val > parent->_val)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;
	}
	//bool Erase(const K& val)
	//{
	//	Node* parent = nullptr;
	//	Node* cur = _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 (parent == nullptr)
	//				{
	//					_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 (parent == nullptr)
	//				{
	//					_root = cur->_left;
	//				}
	//				else
	//				{
	//					if (parent->_left == cur)
	//					{
	//						parent->_left = cur->_left;
	//					}
	//					else
	//					{
	//						parent->_right = cur->_left;
	//					}
	//				}
	//				delete cur;
	//			}
	//			else
	//			{
	//				//都不为空
	//				//找左树的最右节点或者右树的最左节点
	//				Node* parent = cur;
	//				Node* subRight = cur->_left;
	//				while (subRight->_right)
	//				{
	//					parent = subRight;
	//					subRight = subRight->_right;
	//				}
	//				swap(subRight->_val, cur->_val);
	//				if (parent->_left == subRight)
	//				{
	//					parent->_left = subRight->_left;
	//				}
	//				else
	//				{
	//					parent->_right = subRight->_left;
	//				}
	//				delete subRight;
	//			}
	//			return true;
	//		}
	//	}
	//	return false;
	//}

	bool Erase(const K& val)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (val > cur->_val)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (val < cur->_val)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{//准备删除
				
				if (cur->_left == nullptr)//左为空
				{
					if (parent == nullptr)
					{
						_root = cur->_right;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)//右为空
				{
					if (parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else
				{
					//找右子树最左节点
					Node* subLeft = cur->_right;
					Node* parent = cur;
					while (subLeft->_left)
					{
						parent = subLeft;
						subLeft = subLeft->_left;
					}
					swap(subLeft->_val, cur->_val);
					if (parent->_left == subLeft)
					{
						parent->_left = subLeft->_right;
					}
					else
					{
						parent->_right = subLeft->_right;
					}
					delete subLeft;
				}
				return true;
			}
		}
		return false;
	}
	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;
	}
	bool FindR(const K& val)
	{
		return _FindR(_root, val);
	}
	bool InsertR(const K& val)
	{
		return _InsertR(_root, val);
	}
	bool EraseR(const K& val)
	{
		return _EraseR(_root, val);
	}
	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	bool _EraseR(Node*& root, const K& val)
	{
		if (root == nullptr) return false;
		if (root->_val > val)
		{
			return _EraseR(root->_left, val);
		}
		else if (root->_val < val)
		{
			return _EraseR(root->_right, val);
		}
		else
		{
			if (root->_left == nullptr)
			{
				Node* del = root;
				root = root->_right;
				delete del;
			}
			else if (root->_right == nullptr)
			{
				Node* del = root;
				root = root->_left;
				delete del;
			}
			else
			{
				Node* parent = root;
				Node* subRight = root->_left;
				while (subRight->_right)
				{
					parent = subRight;
					subRight = subRight->_right;
				}
				swap(subRight->_val, root->_val);
				_EraseR(root->_left, subRight->_val);
			}
			return true;
		}
	}
	bool _InsertR(Node*& root, const K& val)
	{
		if (root == nullptr)
		{
			root = new Node(val);
			return true;
		}
		if (root->_val > val)
		{
			return _InsertR(root->_left, val);
		}
		else if (root->_val < val)
		{
			return _InsertR(root->_right, val);
		}
		else
		{
			return false;
		}
	}
	bool _FindR(Node* root, const K& val)
	{
		if (root == nullptr) return false;
		if (root->_val > val)
		{
			return _FindR(root->_left, val);
		}
		else if (root->_val < val)
		{
			return _FindR(root->_right, val);
		}
		else
		{
			return true;
		}
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr) return;
		_InOrder(root->_left);
		cout << root->_val << ' ';
		_InOrder(root->_right);
	}
private:
	Node* _root = nullptr;//根节点指针 _root为空代表空树
};
int main()
{
	BSTree<int> bt;
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	for (auto e : a)
	{
		bt.InsertR(e);
	}
	bt.InOrder();

	for (auto e : a)
	{
		bt.Erase(e);
		bt.InOrder();
	}
	return 0;
}

运行结果如下:
在这里插入图片描述
至此,本片文章就结束了,若本篇内容对您有所帮助,请三连点赞,关注,收藏支持下。

创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!

如果本篇博客有任何错误,请批评指教,不胜感激 !!!
在这里插入图片描述

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

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

相关文章

Android Studio 获取 SHA1

以 debug.keystore 调试密钥库为例。 步骤1&#xff1a;明确 debug.keystore 位置 debug.keystore 在 .android 目录下&#xff1a; Windows 用户&#xff1a;C:\Users\用户名\.android\debug.keystore Mac 用户&#xff1a;/Users/用户名/.android/debug.keystore 假设我的…

在Windows中安装Redis

一、下载Redis github链接&#xff1a;https://github.com/redis-windows/redis-windows/releases 二、安装 解压后点击start.bat文件即可启动服务 新开一个cmd窗口进入安装了Redis的文件夹输入redis-cli.exe -h 127.0.0.1 -p 6379连接Redis&#xff0c;见如下结果便是成功&…

数据结构——链式二叉树知识点以及链式二叉树数据操作函数详解!!

引言&#xff1a;该博客将会详细的讲解二叉树的三种遍历方法&#xff1a;前序、中序、后序&#xff0c;也同时会讲到关于二叉树的数据操作函数。值得一提的是&#xff0c;这些函数几乎都是建立在一个函数思想——递归之上的。这次的代码其实写起来十分简单&#xff0c;用不了几…

【Springboot系列】SpringBoot 中的日志如何工作的,看完这一篇就够了

文章目录 强烈推荐引言Spring Boot 中的日志是怎么工作日志框架选择配置文件日志级别自定义日志配置集成第三方日志库实时监控和日志管理 Log4j2工作原理分析1. 核心组件2. 配置文件3. Logger的继承和层次结构4. 日志事件处理流程5. 异步日志 总结强烈推荐专栏集锦写在最后 强烈…

免费图片文字转换成文本,ocr文字识别软件免费版,真的太实用了!

截屏短视频上一段扎心文字&#xff0c;想把它发到朋友圈怎么办&#xff1f;这时候就需要一个OCR识别软件。 它就像一个聪明的小助手&#xff0c;它可以帮助电脑“看懂”书本上或者图片里的字。就像我们用眼睛看字一样&#xff0c;OCR软件用它的“眼睛”扫描图片&#xff0c;识…

C语言代码错误(一)

今天在写选择排序代码时&#xff0c;在测试数据发现不能显示结果 1、代码如下&#xff1a; #include <stdio.h>int main(void) {int i, j; // 循环变量int MinIndex; // 保存最小的值的下标int buf; // 互换数据时的临时变量int n;printf("你想输入多少个数据n:\n…

乐理学习-音及音名

1. 我觉得练习题很重要。我要得到一个反馈 所以我想没学习完书中的一节就要把练习题做下来&#xff0c;虽然慢点也可以。 2. 做个小计划。 今天计算了一下学完《基本乐理-李重光》如果每天3张。也要80天干完&#xff0c;希望能有一天可以学习7张的速度的时候。 3. 练习记录…

【除自身以外数组的乘积】python

目录 思路&#xff1a; 代码&#xff1a; 思路&#xff1a; 直接计算前缀乘积&#xff0c;后缀乘积&#xff0c;然后相乘即可 开始我还在想&#xff0c;遍历一次i&#xff0c;怎么能同时计算前缀乘积和后缀乘积&#xff0c;事实上分开计算比较方便。。 代码&#xff1a; cl…

Spark运行模式详解

Spark概述 Spark 可以在多种不同的运行模式下执行&#xff0c;每种模式都有其自身的特点和适用场景。 部署Spark集群大体上分为两种模式&#xff1a;单机模式与集群模式。大多数分布式框架都支持单机模式&#xff0c;方便开发者调试框架的运行环境。但是在生产环境中&#xff…

聊聊变异测试

软件质量保障 所寫即所思&#xff5c;一个阿里质量人对测试的所感所悟。 1. 介绍 有句话说&#xff1a;证实容易&#xff0c;证伪难。正如测试一样&#xff0c;证明缺陷存在容易&#xff0c;但证明不存在缺陷难。而变异测试颠覆了这一原则&#xff0c;如果我们知道存在缺陷&am…

开发依赖与运行依赖

1. 概念 开发依赖&#xff1a;devDependencies 运行依赖&#xff1a;dependencies 2. 理解 &#xff08;1&#xff09;devDependencies 在线上状态不需要使用的依赖&#xff0c;就是开发依赖。为什么 npm 要把它单独分拆出来呢&#xff1f;最终目的是为了减少 node_modul…

ElasticSearch插件版本与ES版本不对应的解决方案

一、背景 最近需要给es安装ik、hanlp分词器和ingest-attachment管道&#xff0c;服务器已有的es版本为8.5.3&#xff08;似乎太新了&#xff09;&#xff0c;hanlp和ingest-attachment都没有这么高的版本&#xff0c;因此只能下载相对老的版本&#xff0c;然后自己修改配置文件…

Linux定时计划

定时计划 一、计划任务种类 突发性&#xff1a;临时决定只执行一次的任务 at&#xff1a;处理执行一次任务就结束定时性&#xff1a;每隔一定时间需要重复执行此命令 crontab&#xff1a;指定任务&#xff0c;按照设定的周期一直循环执行二、作用 定时任务可以用于自动备份…

Java Swing + MySQL图书借阅管理系统

系列文章目录 Java Swing MySQL 图书管理系统 Java Swing MySQL 图书借阅管理系统 文章目录 系列文章目录前言一、项目展示二、部分代码1.Book2.BookDao3.DBUtil4.BookAddInternalFrame5.Login 三、配置 前言 项目是使用Java swing开发&#xff0c;界面设计比较简洁、适合作…

冯喜运:5.27黄金暴跌大阴后出现“暂定符”今日黄金原油操作策略

【黄金消息面分析】&#xff1a;金价虽然有大阴线暴跌&#xff0c;但依然属于超买后的调整而非熊市&#xff0c;对中长线投资者来说只是市场洗牌。因此&#xff0c;在出现企稳迹象之后&#xff0c;随时关注反弹时机的启动。未来几日&#xff0c;黄金空头可能在进一步发力之前需…

【机器学习结合AI绘画工具】——开启艺术创作的新纪元

目录 一、AI绘画工具的发展历程 二、AI绘画工具的技术原理 实例说明 三、AI绘画工具在艺术创作中的应用 实例网站 四、AI绘画工具的影响与未来展望 结论 机器学习和人工智能&#xff08;AI&#xff09;在过去的十年里取得了显著的进展。特别是在艺术创作领域&#xff0c…

Qt 对话框或者QMainWindow等类中调用自定义QWidget继承组件

简单的方法如下所示 1、创建一个ui文件&#xff0c;界面布局放入QVBoxLayout或者QHBoxLayout 使用他来放入自定义组件&#xff0c;类似如下 2、代码如下&#xff1a; ui.setupUi(this); { //自定义组价如下 KwTable *Table new KwTable(this); ui.vertical…

Java中的类加载器

类加载器 1.什么是类加载器&#xff1f; 启动类加载器&#xff08;Bootstrap ClassLoader&#xff09;&#xff1a;这是JVM自带的类加载器&#xff0c;负责加载Java的核心类库&#xff0c;如rt.jar等。由于安全原因&#xff0c;启动类加载器加载的类不能被其他类加载器加载的类…

python 两个表格字段列名称值,对比字段差异

支持xlsx,xls文件&#xff0c;相互对比字段列 输出两个表格文件相同字段&#xff0c;置底色为绿色 存在差异的不同字段&#xff0c;输出两个新的表格文件&#xff0c;差异字段&#xff0c;置底色为红色 import pandas as pd from openpyxl import load_workbook from openpy…

【云原生】Kubernetes-----POD资源限制与探针机制

目录 引言 一、资源限制 &#xff08;一&#xff09;基本定义 &#xff08;二&#xff09;资源单位 1.CPU资源 2.内存资源 &#xff08;三&#xff09;请求与限制 &#xff08;四&#xff09;定义方式 1.编写yaml文件 2.查看资源情况 &#xff08;五&#xff09;资源…