C++: 二叉搜索树及实现

news2024/9/8 12:16:56

目录

一、二叉搜索树的概念

二、二叉搜索树的操作

2.1插入

2.2删除

1.有左子树,无右子树

2.有右子树,无左子树

3.有左子树和右子树

三、二叉搜索树的实现

要点


前言:为了学习map和set,需要先学二叉搜索树作为铺垫。

一、二叉搜索树的概念

二叉搜索树是一棵二叉树,又称为二叉排序树,它有以下特性:

1.若左子树不为空,则左子树的节点值比根要小

2.若右子树不为空,则右子树的节点值比根要大

3.整棵树都遵循以上规则

需要注意,二叉树也可以是空树。

二、二叉搜索树的操作

2.1插入

插入的思路是,先用递归将树搜索一遍,用key和树的根的节点比大小,比它大往右走,比它小往左走,直到走到空,那么就在空的位置插入。

2.2删除

删除的思路比较复杂。删除情况分三种,分别是所要删除的节点情况,若删除的是key节点:

1.有左子树,无右子树

这里就将key节点的前一个节点,指向它的后一个节点,然后释放key节点。

2.有右子树,无左子树

和上面类似。

3.有左子树和右子树

这里比较复杂,需要将key节点与它的左子树中的最大的一个,或者右子树中最小的一个交换,然后再删除key。

右子树中最小的一个,是第一个右节点的最左的节点。若没有最左节点,那就只能和右节点交换。

交换以后,就可以删除key节点也就是4了。

最后一种无左子树也无右子树可以放在1和2中讨论,归为一种。操作比较简单,此处不再赘述。

三、二叉搜索树的实现

要点

1.为了体现封装,我们只留接口放在类的public中,把具体实现的细节放在private中,这样也方便代码日后维护。同时由于使用递归,所以需要取root的引用,参数就要给root,而在类外是没有办法获取作为私有的root的,因此只能把实现的细节写在private里。

2.搜索二叉树可以用来匹配,比如说当字典使用和门禁系统,这里可以再放入一个类模板。

3.插入的时候要新建一个节点再插入,这里会改变root的地址,所以传引用会更好,不然就要传二级指针了,这个很麻烦,而且在C++中是避免的。

4.删除的时候,要先找到这个节点,再删除。删除时,一共涉及三个指针的操作。建议先理清思路再写代码。

namespace ting
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode(const K& content, const V& value)
			:_content(content)
			,_left(nullptr)
			,_right(nullptr)
			,_value(value)
		{

		}
		K  _content;
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;

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

		bool Insert(const K& key, const V& value)
		{
			return _Insert(_root, key, value);
		}
		Node* Find(const K& key)
		{
			return _Find(_root, key);
		}

		bool Erase(const K& key)
		{
			return _Erase(_root, key);
		}
		
		void InOrder()
		{
			_InOrder(_root);
		}
		
	private:
		Node* _root = nullptr;

		Node* _Find(Node* root, const K& key)
		{
			while (root != nullptr)
			{
				if (root->_content < key)
				{
					root = root->_right;
				}
				else if (root->_content > key)
				{
					root = root->_left;
				}
				else if (root->_content == key)
				{
					return root;
				}
			}
			return nullptr;
		}

		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_content<<" "<<root->_value << endl;
			_InOrder(root->_right);
		}


		bool _Insert(Node*& root, const K& key, const V& value)
		{
			if (root == nullptr)
			{
				/*root->_content = key;
				root->_value = value;
				return true;*/
				root = new Node(key, value);//这里要给root建一个节点
				return true;
			}
			
			if (root->_content < key)
			{
				return _Insert(root->_right, key, value);
			}
			else if (root->_content > key)
			{
				return _Insert(root->_left, key, value);
			}
			else
			{
				return false;
			}
		}

		bool _Erase(Node*& root, const K& key)
		{
			if (root == nullptr)
				return false;
			
			if (root->_content < key)
			{
				return _Erase(root->_right, key);
			}
			else if (root->_content > key)
			{
				return _Erase(root->_left, key);
			}
			else
			{
				Node* del = root;
				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else
				{
					Node* prev = root;
					Node* minNode = root->_right;
					while (minNode->_left != nullptr)
					{
						prev = minNode;
						minNode = minNode->_left;
					}

					root->_content = minNode->_content;
					root->_value = minNode->_value;

					if (prev->_left == minNode)
					{
						prev->_left = minNode->_right;
					}
					else
					{
						prev->_right = minNode->_right;
					}

					del = minNode;
				}
				delete del;
				return true;		
			}

		}
	};

	void TestBSTree()
	{
		BSTree<string, string> dict;
		dict.Insert("insert", "插入");
		dict.Insert("erase", "删除");
		dict.Insert("left", "左边");
		dict.Insert("string", "字符串");

		string str;
		while (cin >> str)
		{
			auto ret = dict.Find(str);
			if (ret)
			{
				cout << str << ":" << ret->_value << endl;
			}
			else
			{
				cout << "单词拼写错误" << endl;
			}
		}

		string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
		// 统计水果出现的次
		BSTree<string, int> countTree;
		for (auto str : strs)
		{
			auto ret = countTree.Find(str);
			if (ret == NULL)
			{
				countTree.Insert(str, 1);
			}
			else
			{
				ret->_value++;
			}
		}
		countTree.InOrder();
	}
}

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

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

相关文章

计算机毕业设计 | SpringBoot招投标系统 任务发布网站(附源码)

1&#xff0c;绪论 在市场范围内&#xff0c;任务发布网站很受欢迎&#xff0c;有很多开发者以及其他领域的牛人&#xff0c;更倾向于选择工作时间、工作场景更自由的零工市场寻求零散单子来补贴家用。 如今市场上&#xff0c;任务发布网站鱼龙混杂&#xff0c;用户需要找一个…

AIGC 008-IP-Adapter文本兼容图像提示适配器用于文本到图像扩散模型

AIGC 008-IP-Adapter文本兼容图像提示适配器用于文本到图像扩散模型&#xff01; 文章目录 0 论文工作1 论文方法2 效果 0 论文工作 这篇论文介绍了 IP-Adapter&#xff0c;一种 高效地将预训练的图像到图像转换模型适应到新领域 的方法。它通过在预训练模型的 输入端 添加一个…

Python考试复习--day2

1.出租车计费 mile,waitmap(int,input().split(,)) if mile<3:money13wait*1 elif mile>3 and mile<15:money13(mile-3)*2.3wait*1 else:money1312*2.3(mile-15)*2.3*(10.5)wait*1 print({:.0f}.format(money)) 【知识点1】&#xff1a; map() 函数 【知识点1】&…

数字水印 | 离散余弦变换 DCT 基本原理及 Python 代码实现

目录 1 基本原理2 代码实现3 图像压缩 1 基本原理 参考博客&#xff1a;https://www.cnblogs.com/zxporz/p/16072580.html D C T \mathsf{DCT} DCT 全称为 D i s c r e t e C o s i n e T r a n s f o r m \mathsf{Discrete\ Cosine\ Transform} Discrete Cosine Transfo…

HTTPS证书——网站如何实现HTTPS访问?

实现网站HTTPS访问可以简化为以下四个基本步骤&#xff0c;确保过程既通俗易懂又条理清晰&#xff1a; 1. 申请SSL证书 - 目的&#xff1a;SSL证书是实现HTTPS加密的关键&#xff0c;它验证了网站的身份&#xff0c;并提供了加密数据所需的密钥。 - 操作&#xff1a;首先&…

面试手撕——使用两个线程交替打印1-100

记录一下使用两个线程交替打印1-100的操作: /*** description: 使用两个线程交替打印1-100* author: Jay* create: 2024-05-27 21:29**/ public class print_1_to_100 {static volatile int flag 1; //此处需要加关键字volatile保证变量之间的可见性&#xff0c;否则程序将会…

TS(TypeScript)中Array数组无法调出使用includes方法,显示红色警告

解决方法 打开tsconfig.json文件&#xff0c;添加"lib": ["es7", "dom"]即可。 如下图所示。

move base全解

0. 简介 之前我们专门有一节讲到了《move_base源码学习》。主要介绍了MoveBase基类中函数的大概意思以及调用的方式。move_base是ROS下关于机器人路径规划的中心枢纽。它通过订阅激光雷达、map地图、amcl的定位等数据&#xff0c;然后规划出全局和局部路径&#xff0c;再将路径…

数组-两个升序数组中位数

一、题目描述 二、解题思路 (一).基本思想&#xff1a; 如果列表总长度allsize( arr1.size()arr2.size() ) 为奇数时&#xff0c;中位数位置应该在两个列表排序后的第 allsize/2 位置处&#xff0c;如果allsize为偶数&#xff0c;中位数应该取 (allsize/2)-1 和 allsize/2 的…

python数据类型之列表

目录 1.创建列表 2.列表基础操作 常用操作 对列表元素顺序随机打乱 列表下标和切片 字符串分割为列表 列表位移 列表切片替换 3.列表内置方法 4.列表排序 简单排序 使用key参数按指定规则排序 二维列表排序 自定义排序规则函数 5.列表排序算法 选择排序 柱状图…

4-主窗口

4-主窗口 1、简介2 菜单栏、工具栏、状态栏2.1 菜单栏2.2 QAction2.3 工具栏2.4 状态栏 3 混合方式UI设计 1、简介 QMainWindow是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏、多个工具栏、多个停靠控件、一个状态栏以及一个中心控件&#xff0c;是许多应用程序&…

宝塔部署前后端分离项目手册

文章目录 安装宝塔安装环境开始部署1. 前端Vue项目1.先本地启动前端项目&#xff08;记住端口号&#xff09;2.打包前端项目3.上传前端项目4.创建PHP站点5.安全里开放端口号6.测试前端 2. 后端boot项目1. 先在本地跑起来2.修改数据库的配置信息3. 项目打包4. nohup启动项目4.1 …

2024年必看!会声会影神器升级,让你的视频制作技能直线上升

在数字媒体内容呈现爆炸式增长的今天&#xff0c;无论是个人还是企业&#xff0c;都开始重视视频制作与编辑的质量。一款优秀的视频编辑软件&#xff0c;不仅需要具备强大的功能&#xff0c;更需要提供直观、高效的用户体验。在这样的背景下&#xff0c;会声会影2024应运而生&a…

《开发问题解决》Window下7z解压:cannot create symbolic link : 客户端没有所需的特权

问题描述&#xff1a; 今天使用7z来解压东西的是突然出现这个问题。 问题解决&#xff1a; download直接下载到c盘中&#xff0c;由于所在文件夹有权限限制。无法进行正常解压。 7.zip解压时使用管理员权限进行解压&#xff0c;解压时使用管理员权限。即如图 使用管理员权限重…

基于飞书机器人跨账号消息提醒

事情的起因是飞书中不同的账号不能同时登录&#xff0c;虽然可以在飞书的账号切换页面看到其他账号下是否有消息提醒&#xff08;小红点&#xff09;&#xff0c;但是无法实现提醒功能&#xff0c;很不优雅&#xff0c;因此本文尝试提出一种新的方式实现不同账号之间的提醒功能…

基于Go实现的分布式主键系统

基于Go实现的分布式主键系统 摘要 随着互联网的发展&#xff0c;微服务得到了快速的发展&#xff0c;在微服务架构下&#xff0c;分布式主键开始变得越来越重要。目前分布式主键的实现方式颇多&#xff0c;有基于数据库自增的、基于UUID的、基于Redis自增的、基于数据库号段的…

时空AI软件:地理信息与遥感领域的智慧引擎

在地理信息与遥感技术的广阔疆域&#xff0c;时空AI软件如同一颗璀璨新星&#xff0c;将时空信息与智能深度融合&#xff0c;驱动着地理信息分析、决策支持、环境监测、城市规划等领域的深刻变革。本文将深入剖析其技术核心、应用实例、未来趋势&#xff0c;探索时空AI软件如何…

OrangePi Kunpeng Pro 开发板测评及Python开发实测

一、背景 首先感谢 创新乐知通过CSDN 邀请本人&#xff0c;参与这次 评测活动。这块开发板是香橙派联合华为精心打造&#xff0c;具有超强算力的鲲鹏开发板。本人使用最多的还是树莓派系列的板子&#xff0c;国产板子特别是华为为核心的板子还是头一次使用&#xff0c;特别感兴…

使用 Spring Cloud Alibaba AI 构建 RAG 应用

作者&#xff1a;姬世文 背景介绍 RAG&#xff08;Retrieval Augmented Generation&#xff09; 检索增强生成&#xff08;RAG&#xff09;是一种用于将数据与人工智能模型集成的技术。在 RAG 工作流程中&#xff0c;第一步将文档数据加载到矢量数据库&#xff08;例如 Redi…

【Day8:JAVA字符串的学习】

目录 1、常用API2、String类2.1 String类的特点2.2 String类的常见构造方法2.3 String类的常见面试题&#xff1a;2.3.1 面试题一&#xff1a;2.3.2 面试题二&#xff1a;2.3.3 面试题三&#xff1a;2.3.4 面试题四&#xff1a; 2.4 String类字符串用于比较的方法2.5 String类字…