【C++进阶03】二叉搜索树

news2025/1/15 17:50:52

在这里插入图片描述

一、二叉搜索树的概念和性质

中序遍历二叉搜索树会得到一个有序序列
所以二叉搜索树又称二叉排序树
它可以是一棵空树
也可以是具有以下性质的二叉树:

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

二叉搜索树没有相同值的节点
二叉搜索树支持增删查,不支持改
修改会破坏二叉搜索树跟节点比左子树大
右子树小的结构

如图:
在这里插入图片描述

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

二叉搜索树节点

// BSTree.h
#pragma once

// BinarySearchTree --- BSTree
template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

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

二叉搜索树的插入、中序遍历和查找

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node; // 名字过长在类里面再typedef,类里受类域限制不会名字冲突
public:
	bool Insert(const K& key) // 要插入节点内容重复,插入失败返回false
	{
		if (_root == nullptr)
		{
			_root = new Node(key); // 没写构造函数会导致无法将参数 1 从“const K”转换为“const BSTreeNode<K> &”,new的时候会以为你要调转换
			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;
	}

	void InOrder() // 中序需要传_root根节点,在类外无法直接访问,所以再套一层。不能用缺省值解决
	{
		_InOrder(_root);
	}

	// void _InOrder(Node* root = _root) // 缺省值必须是全局变量或是常量,访问_root得用this,而this只能在函数内部使用
	void _InOrder(Node* root) // 中序
	{
		if (root == nullptr)
			return;

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

private:
	Node* _root = nullptr;
};

插入的递归实现

bool _InsertR(Node*& root, const K& key) // 用&解决链接问题,最后走到nullptr的位置是上一个节点的别名,很巧妙的链接上
{
	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 InsertR(const K& key)
{
	return _InsertR(_root, key);
}

查找的递归实现

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

	if (root->_key == key)
		return true;

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

	return false;
}

bool FindR(const K& key) // 递归查找
{
	return _FindR(_root, key);
}

二叉搜索树的难点在于删除
分别有三种情况

  1. 被删的节点没有孩子节点
  2. 被删的节点只有左孩子或右孩子

第一种情况直接删不用特殊处理
第二种情况将被删节点的孩子节点
连接到他的父节点即可

在这里插入图片描述
3. 被删的节点有两个孩子节点

这时被删的节点的父节点
无法接管他的两个孩子节点
解决方法:
请一个节点替自己接管自己的两个孩子
这个节点可以是左子树最大的节点 or
右子树最小节点

删除接口代码实现

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 = _root->_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 = _root->_left;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			// 3. 左右都不为空
			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;
}

删除递归实现

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* MaxLeft = root->_left;
			while (MaxLeft->_right)
			{
				MaxLeft = MaxLeft->_right;
			}

			swap(root->_key, MaxLeft->_key);

			return _EraseR(_root->_left, key); // 转换成子树去删除
		}

		delete del;
		return true;
	}
}

✨✨✨✨✨✨✨✨
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
✨✨✨✨✨✨✨✨

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

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

相关文章

技术经验|Java基础之LocalTime类

文章目录 1 背景2 Time包在实际开发中的位置3 本地日期和时间类3.1 LocalTime自带功能3.2 LocalTime继承、重写功能 4 总结 &#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f525; 三连支持&a…

【Java系列】多线程案例学习——基于阻塞队列实现生产者消费者模型

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习JavaEE的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录…

九九乘法表c 语言 用于打印九九乘法表

以下是一个简单的C语言程序&#xff0c;用于打印九九乘法表&#xff1a; #include <stdio.h>int main() {int i, j;for (i 1; i < 9; i) {for (j 1; j < i; j) {printf("%d*%d%-2d ", j, i, i*j);}printf("\n");}return 0; }解释&#xff1…

GBASE南大通用GBaseCommand 类

代表一个要对 GBASE南大通用数据库执行操作的 SQL 语句。这个类不能被继承。对 于该类所有成员的列表&#xff0c;参考 GBASE南大通用 GBaseCommand 成员。  继承层次 System.Object |__ System.MarshalByRefObject |__ System.ComponentModel.Component |__ System.D…

go语言,ent库与gorm库,插入一条null值的time数据

情景介绍 使用go语言&#xff0c;我需要保存xxxTime的字段至数据库中&#xff0c;这个字段可能为空&#xff0c;也可能是一段时间。我采取的是统一先赋值为空&#xff0c;若有需要&#xff0c;则再进行插入&#xff08;需要根据另一个字段判断是否插入&#xff09; 在我的数据…

‘>>’,‘<<’和‘’的使用

>>: n n >> 1 是使用位移运算符将 n 向右移动一位的操作。在 C 语言中&#xff0c;>> 是右移位运算符&#xff0c;表示将二进制数向右移动指定的位数。 对于无符号整数&#xff0c;在右移位操作时&#xff0c;高位用 0 填充。例如&#xff0c;如果 n 的二…

Navicat导入与导出表的操作流程

我们使用Navicat时&#xff0c;创建表有两种方法&#xff0c;一种是写SQL语句&#xff0c;这种方法适合数据较少的表&#xff1b;另一种是通过“导入向导”功能导入表&#xff0c;这种方法可以一次性导入大批量的数据&#xff0c;提高效率。 下面我以.xls格式为例&#xff0c;…

程序员的浪漫,2023跨年烟花代码(Python)

跨年倒计时啦 今天分享用python实现一场烟花秀 感兴趣的小伙伴&#xff0c;提前收藏起来&#xff0c;一定要看到最后&#xff01; 话不多说 1.首先制作一个绚丽的夜空 制作夜空&#xff0c;其实就是设置画布。大家知道&#xff0c;夜晚的天空月明如水&#xff0c;星光攒动&a…

创建型设计模式 - 抽象工厂模式 - JAVA

创建型设计模式 - 抽象工厂设计模式 一. 简介二. 列子2.1 定义电脑的抽象类和子类2.2 定义抽象工厂类和其实现类2.3 测试 三. 抽象工厂设计模式的好处四. 抽象工厂模式的案例 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续…

git 如何将某个分支的某个提交复制到另外一个分支

请直接去看原文: 原文链接:git 如何将某个分支的某个提交复制到另外一个分支_gitlab里面的markdown文件可以复用其他分支的吗-CSDN博客 --------------------------------------------------------------------------------------------------------------------------------…

drf知识-08

Django之了解DRF框架 # 介绍&#xff1a;DRF全称 django rest framework # 背景&#xff1a; 在序列化与反序列化时&#xff0c;虽然操作的数据不尽相同&#xff0c;但是执行的过程却是相似的&#xff0c;也就是说这部分代码是可以复用简化编写的 增&#xff1a;校验请…

leaflet学习笔记-地图图层控制(二)

图层介绍 Leaflet的地图图层控件可控制两类图层&#xff1a;一类是底图图层&#xff08;Base Layers&#xff09;&#xff0c;一次只能选择一个图层作为地图的背景图层&#xff0c;即底图图层&#xff0c;在地图图层控件中用单选按钮控制&#xff1b;另一类是覆盖图层&#xff…

目标检测损失函数:IoU、GIoU、DIoU、CIoU、EIoU、alpha IoU、SIoU、WIoU原理及Pytorch实现

前言 损失函数是用来评价模型的预测值和真实值一致程度&#xff0c;损失函数越小&#xff0c;通常模型的性能越好。不同的模型用的损失函数一般也不一样。损失函数主要是用在模型的训练阶段&#xff0c;如果我们想让预测值无限接近于真实值&#xff0c;就需要将损失值降到最低…

Ubuntu18.04安装GTSAM库并验证GTSAM是否安装成功(亲测可用)

在SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;和SFM&#xff08;Structure from Motion&#xff09;这些复杂的估计问题中&#xff0c;因子图算法以其高效和灵活性而脱颖而出&#xff0c;成为图模型领域的核心技术。GTSAM&#xff08;Georgia Tech Smo…

【论文阅读】Resource Allocation for Text Semantic Communications

这是一篇关于语义通信中资源分配的论文。全文共5页&#xff0c;篇幅较短。 目录在这里 摘要关键字引言语义通信资源分配贡献公式符号 系统模型DeepSC TransmitterTransmission ModelDeepSC Receiver 语义感知资源分配策略Semantic Spectral Efficiency &#xff08;S-SE&#…

Docker七 | 搭建Swarm集群

目录 创建Swarm集群 创建管理节点 增加工作节点 查看集群 部署服务 新建服务 查看服务 服务伸缩 增加服务 减少服务 删除服务 创建Swarm集群 创建管理节点 在192.168.117.131下执行docker swarm init命令的节点自动成为管理节点 [rootlocalhost ~]# docker swar…

idea中切换JDK8、JDK11、JDK17

有时候&#xff0c;我们可能需要在不同的Java版本中去测试或者查看源码&#xff0c;idea可以让我们修改Java的版本。 前提&#xff1a;你必须下载安装好对应的Java版本&#xff0c;可参考文章【windows下切换JDK8、JDK11、JDK17】&#xff08;https://blog.csdn.net/xijinno1/a…

深度学习核心技术与实践之深度学习基础篇

非书中全部内容&#xff0c;只是写了些自认为有收获的部分 神经网络 生物神经元的特点 &#xff08;1&#xff09;人体各种神经元本身的构成很相似 &#xff08;2&#xff09;早期的大脑损伤&#xff0c;其功能可能是以其他部位的神经元来代替实现的 &#xff08;3&#x…

腾讯云服务器怎么买划算?最新优惠价格表

2023腾讯云轻量应用服务器优惠价格表&#xff0c;12月最新报价&#xff0c;腾讯云轻量2核2G3M带宽62元一年、2核2G4M轻量服务器118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;756元三年、4核8G12M轻量服务器646元15个月&#xff0c;CVM云服务器S5实例2核2G…

C# 常用数据类型及取值范围

1.常见数据类型和取值范围 序号数据类型占字节数取值范围1byte10 到 2552sbyte1-128 到 1273short 2-32,768 到 32,7674ushort20 到 65,5355int4-2,147,483,648 到 2,147,483,6476uint40 到 4,294,967,2957float41.5 x 10−45 至 3.4 x 10388double85.0 10−324 到 1.…