C++实现基础二叉搜索树(并不是AVL和红黑树)

本次实现的二叉搜索树并不是AVL数和红黑树,只是了解流程和细节。

目录

  • 二叉搜索树的概念
  • K模型二叉搜索树的实现
    • 二叉搜索树的架构
    • insert插入
    • find 查找
    • 中序遍历Inorder
    • 删除earse
      • 替换法的思路
      • 情况一 :假如要删除节点左边是空的。
        • 在左边时
        • 在右边时
      • 情况二:假如要删除节点右边是空的。
        • 是父亲的右边时
        • 是父亲的左边时
      • 情况三:两边都有值
        • 左边
        • 右边
        • 代码实现

二叉搜索树的概念

二叉搜索树并不是单纯存储数据。所以他有规则:
①.左子树比根小,右子树比根大。

②.搜索二叉树不建议用递归。

二叉搜索树一定要遵循这个规律!否则他都不能算是二叉搜索树!

K模型二叉搜索树的实现

二叉搜索树的架构

我们之前也学过二叉树,二叉树的结构是节点里面放了左右指针和值,所以节点就是一下结构。 而二叉树的本身就是又一个根节点连接起来的

template<class T>
struct BinarySearchTreeNode
{
	BinarySearchTreeNode(T& key)
		:_key(key)
		, _left(nullptr)
		, _right(nullptr)
	{}
	T _key;
	BinarySearchTreeNode* _left;
	BinarySearchTreeNode* _right;
};
	template<class T>
	class BinarySearchTree
	{
	public:
		typedef BinarySearchTreeNode<T> Node;		
	private:
		Node* _root=nullptr;
	};

insert插入

插入一定要遵循规则:
①.左子树比根小,右子树比根大。
不是这个规则都不是二叉搜索树。

(1). 当你插入第一个值的时候,他就是根,所以我们要特殊处理,当_root==nullptr时,要把第一个插入的值变成根。
(2).除第一个值之后的值就要遵行规则。

bool insert(T& x)
{
	if (_root == nullptr)
	{
		Node* noeNode = new Node(x);
		_root = noeNode;
		return true;
	}
	Node* cur = _root;
	Node* parent = cur;
	while (cur)
	{
		if (cur->_key < x)
		{
			//大于在右边
			parent = cur;
			cur = cur->_right;

		}
		else if (cur->_key > x)
		{
			//小于在左边
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//等于,二叉搜索树不允许冗余,所以直接返回false。
			return false;
		}

	}
	Node* newNode = new Node(x);
	if (newNode->_key > parent->_key)
		parent->_right = newNode;
	else
		parent->_left = newNode;
	return true;
}

find 查找

查找就很简单,只要把前面的代码复制一半,当查找到了,我们就返回true,到空了都没找到就返回false。

bool find(const T& x)
{
	Node* cur = _root;
	Node* parent = cur;
	while (cur)
	{
		if (cur->_key < x)
		{
			//大于在右边
			parent = cur;
			cur = cur->_right;

		}
		else if (cur->_key > x)
		{
			//小于在左边
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//等于,找到了
			return true;
		}

	}
	return false;
}

中序遍历Inorder

中序遍历我们都很了解了,但是问题是,我们要把根传入,根一定是私有的,你在类外访问不了。这个时候我们就可以套一层。

publicvoid Inorder()
{
	_Inorder(_root);
	cout << endl;
}
private:
	void _Inorder(Node* _root)
	{
		if (_root == nullptr) return;
		_Inorder(_root->_left);
		cout << _root->_key<< " ";
		_Inorder(_root->_right);
	}
	Node* _root=nullptr;

删除earse

删除其实很麻烦,有很多种情况。因为二叉搜索数一定是满足规则的,所以我们删除不能混乱了规则,而是继续保持规则。所以我们要用替换法,保证规则不乱

替换法的思路

因为我们要保证规则不乱,那么我们可以用替换法,让左树的最大值/右树的最小值与当前值替换,因为当前节点的左边一定比他小,右边一定比他大,那么当我们用左边最大值,替换了当前节点,也不会改变规则;右边同理。

情况一 :假如要删除节点左边是空的。

在左边时

假如我要删除9这个节点。并且左侧是空,那么我们是不是可以直接让他父亲的左边指向他的右边?思考一下!

为什么?左侧为空,当前删除节点的左侧一定是什么都没有的,右边可有可无,但是右边没有也没关系,一样置空,有的话,我们就需要让他的父亲链接他的孩子。
在这里插入图片描述
并且我们发现并不用替换法,就可以直接链接后删除。
在这里插入图片描述

在右边时

其实在右边也是一样的,只不是我们要特殊判断一下,删除节点时父亲的左边还是右边,方便我们链接。
在这里插入图片描述

情况二:假如要删除节点右边是空的。

是父亲的右边时

那么他右边一定是没有值的,左边的值可有可无,那么我们就需要让它的父亲指向他的左值就可以了。
在这里插入图片描述

是父亲的左边时

在这里插入图片描述

情况三:两边都有值

如果这棵树是这样的,那么我要删除9怎么做呢?就需要替换法,我们需要找左边最大值或者右边最小值,我们假设要找是右边最小值
在这里插入图片描述

从当前节点开始找,右边最小值是10(蓝色标记)。

问:为什么要从当前节点找,而不是从根开始?
答:如果从根开始找,你会发现,当我们交换后,不满足条件了。图中右边最小值是13,如果换到最左边那就不满足二叉搜索的要求了,所以要从当前节点找,因为当前节点的右边跟当前这个位置换一定是比大,比其他节点小的。

在这里插入图片描述
当我们都找到了,我们就要用交换法。将两个值交换后,左边最小的那个节点就是我们要删除的。那么我们就需要把最小节点的右边给它的右边。
在这里插入图片描述

左边

这里给大家道个歉,因为数字太过紧凑,所以凑不出数,只能让整型和浮点数混合了,在实际场景中是不可能有的,只是举个例子

假设我们交换后,发现要删除的节点右边还有值,

问题:我怎么让被删除节点的父亲知道是左边还是右边的节点间接我的右节点呢
答:需要判断一下,如果被删除的节点是父亲的左边,就需要让左边指向被删的右边,如果是父亲的右边就让父亲的右边指向被删的右边。

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

右边

这种情况,就是他在父亲的右边,所以我们需要让父亲的右边链接被删的右边。
在这里插入图片描述

代码实现

虽然删除的代码很复杂,但是要注意的细节很多。
(1).在我们找到了当前被删节点,情况三的时候,能不能让Node* rightMinParent = nullptr;? 答:不可以,因为当我们删除这种情况的时候,就会导致空指针访问,因为循环我们没有进去,但是链接值的时候,就会导致空指针访问。

(2).在我们交换后,能不能递归把他删了?不能!我问你交换后还满足二叉搜索数的要求吗?不满足,你永远都不会找到!

在这里插入图片描述

		bool erase(const T& x)
		{
			Node* cur = _root;
			Node* parent = cur;
			while (cur)
			{
				if (cur->_key < x)
				{
					//大于在右边
					parent = cur;
					cur = cur->_right;

				}
				else if (cur->_key > x)
				{
					//小于在左边
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//等于,找到了

					if (cur->_left == nullptr)
					{
						//没有值直接删,把右边给父亲
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
							delete cur;
						}
						else if(cur == parent->_right)
						{
							parent->_right = cur->_right;
							delete cur;
						}
					}
					else if (cur->_right == nullptr)
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
							delete cur;
						}
						else if (cur == parent->_right)
						{
							parent->_right = cur->_left;
							delete cur;
						}
					}
					else
					{
						Node* rightMin = cur->_right;
						Node* rightMinParent = cur;
						//这里要让父亲是cur,删根的时候就会出问题
						while (rightMin->_left)
						{
							rightMinParent = rightMin;
							rightMin = rightMin->_left;
						}
						std::swap(cur->_key, rightMin->_key);
						if (rightMinParent->_left == rightMin)
							rightMinParent->_left = rightMin->_right;
						else
							rightMinParent->_right = rightMin->_right;
						delete rightMin;
					}
					return true;
				}
			}
			return false;
		}

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

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

相关文章

JavaScript-数组的增删改查

数组的操作一共有四种&#xff1a; 查询数组数据修改数组中元素的值数组添加新的数据删除数组中的元素 数组的初始化 有些编程语言的数组初始化是用{}包着的&#xff0c;而JS的数组初始化用[] let num[2,6,1,77,52,25,7]; 数组的查询 想要具体查询数组中的某个元素 可以用数…

【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

文章目录 推送模式本地文件持久化&#xff08;拉模式&#xff09;配置yml编写处理类添加配置演示 配置中心持久化&#xff08;推模式&#xff09;修改nacos在sentinel中生效引入依赖配置文件 修改sentinel在nacos中生效下载源码更改代码演示 总结 推送模式 Sentinel 规则的推送…

【JavaEE 初阶(十)】JVM

❣博主主页: 33的博客❣ ▶️文章专栏分类:JavaEE◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多进阶知识 目录 1.前言2.JVM内存区域划分3.类加载3.1双亲委派模型 4.垃圾回收&#xff08;GC&#xff0…

结构体变量的创建和初始化以及内存对齐

前言 嗨&#xff0c;我是firdawn&#xff0c;在本章中我们将介绍&#xff0c;结构体变量的创建和初始化&#xff0c;结构成员访问操作符以及结构体的内存对齐&#xff0c;下面是本章的思维导图&#xff0c;接下来&#xff0c;让我们开始今天的学习吧&#xff01; 一&#xf…

下载CentOS系统或者下载Ubuntu系统去哪下?

因为Centos官网是挂在国外的服务器上&#xff0c;下载镜像时相比于国内的下载速度会慢很多&#xff0c;分享国内的镜像站去阿里巴巴下载Centos镜像。 首先分享两种下载方式&#xff0c;如果只想下载Centos那么就访问方式一的下载地址即可&#xff0c;如果还想下载其他的系统&a…

AI大模型探索之路-实战篇5: Open Interpreter开放代码解释器调研实践

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;DB-GPT数据应用开发框架调研实践 目录 系列篇章&#x1f4a5;前言一、何为Open Interpreter&#xff1f;二、与 ChatGPT 的代码解释器比较三、 Open Interpreter的特性1、强大的本地计算能力2、丰富的功能3、高度的…

基于springboot+vue的招聘信息管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

生产物流智能优化系统

对生产调度、物流调度【车辆路径问题、配送中心拣选问题】智能优化算法研究形成系统性程序&#xff0c;逐步开发设计一个智能优化系统【包括&#xff1a;问题说明、实验界面、算法结构和算法程序应用说明】&#xff0c; 当前完成TSP和集送车辆路径的算法程序&#xff0c;程序效…

产品经理-需求分析(三)

1. 需求分析 从业务的需要出发&#xff0c;确定业务目的和目标&#xff0c;将业务需求转为产品需求 1.1 业务需求 业务需求 业务动机 业务目标 就是最根本的动机和目标成果&#xff0c;通过这个需求解决特定的问题 1.2 产品需求 产品需求 解决方案 产品结构 产品流程…

Java进阶学习笔记8——单继承、Object类、方法重写

Java 是单继承的&#xff0c;Java中的类不支持多继承&#xff0c;但是支持多层继承。 Object类是所有类的父类。 Java不支持多类继承&#xff1a; Java支持多层继承&#xff1a; 反证法&#xff1a; Object类&#xff1a; Object类是java所有类的祖宗类&#xff0c;我们写的任…

Excel中Lookup函数

#Excel查找函数最常用的是Vlookup&#xff0c;而且是经常用其精确查找。Lookup函数的强大之处在于其“二分法”的原理。 LOOKUP&#xff08;查找值&#xff0c;查找区域&#xff08;Vector/Array&#xff09;&#xff0c;[返回结果区域]&#xff09; 为什么查找区域必须升序/…

2024年全国大学生电工数学建模竞赛B题解析 | 数据处理 代码 论文分享

B 题&#xff1a;大学生平衡膳食食谱的优化设计及评价 1 数据预处理2 问题一2.1 问题1.12.1.1 评价体系的构建2.1.2 指标计算2.1.3 指标计算结果2.1.4 基于层次分析法的膳食营养评价模型2.1.5 评价模型的求解 2.2 问题1.22.2.1 食物与成分间拓扑关系的构建2.2.2 微调模型的建立…

内网(极空间)搭建gitlab跳板机转发端口及域名配置

背景说明 https://blog.csdn.net/GodDavide/article/details/139182475 上文说到: 我已经用docker搭好了gitlab-ce服务&#xff0c;但我是部署在自己的家庭nas-极空间z4pro里的&#xff0c;属于内网环境。 另外我有一台阿里云服务器&#xff0c;做跳板机。 我有一个阿里的域名…

跟TED演讲学英文:Bring on the learning revolution! by Sir Ken Robinson

Bring on the learning revolution! Link: https://www.ted.com/talks/sir_ken_robinson_bring_on_the_learning_revolution Speaker: Sir Ken Robinson Date: February 2010 文章目录 Bring on the learning revolution!IntroductionVocabularySummaryTranscriptAfterword I…

基于DdddOcr通用验证码离线本地识别SDK搭建个人云打码接口Api

前言 最近介绍了一款免费的验证码识别网站,识别效率太低,考虑到ddddocr是开源的,决定搭建搭建一个,发现原作者sml2h3已经推出好久了,但是网上没有宝塔安装的教程,于是本次通过宝塔搭建属于自己的带带弟弟OCR通用验证码离线本地识别 原项目地址:https://github.com/sml2…

Project Reactor 响应式编程

Project Reactor 响应式编程 什么是响应式编程 响应式编程&#xff08;Reactive Programming&#xff09;是一种编程范式&#xff0c;致力于处理异步数据流和变化。它的核心思想是构建响应于变化的系统&#xff0c;即当数据流或事件发生变化时&#xff0c;系统能够自动地调整…

【研发日记】【策划向】(一)游戏策划其实就是一道加减法题

文章目录 序设计的过程其实是控制自己欲望的过程我海纳百川&#xff0c;你要不要看看&#xff1f;我跟别人不一样&#xff01;我的人设就是没有人设&#xff0c;或者说任何人设都是我的人设 记 序 不知不觉进入这个行业几年了&#xff0c;也经历了独立开发和团队开发的过程。在…

【第1章】SpringBoot入门

文章目录 前言一、版本要求1. SpringBoot版本2. 其他2.1 System Requirements2.2 Servlet Containers2.3 GraalVM Native Images 3. 版本定型 二、新建工程1.IDEA创建 ( 推荐 ) \color{#00FF00}{(推荐)} (推荐)2. 官方创建 三、第一个SpringBoot程序1. 引入web2. 启动类3. 启动…

【Spring】SSM介绍_SSM整合

1、SSM介绍 1.1简介 SSM&#xff08;Spring SpringMVC MyBatis&#xff09;整合是一种流行的Java Web应用程序框架组合&#xff0c;它将Spring框架的核心特性、SpringMVC作为Web层框架和MyBatis作为数据访问层框架结合在一起。这种整合方式提供了从数据访问到业务逻辑处理再…

【Text2SQL】WikiSQL 数据集与 Seq2SQL 模型

论文&#xff1a;Seq2SQL: Generating Structured Queries from Natural Language using Reinforcement Learning ⭐⭐⭐⭐⭐ ICLR 2018 Dataset: github.com/salesforce/WikiSQL Code&#xff1a;Seq2SQL 模型实现 一、论文速读 本文提出了 Text2SQL 方向的一个经典数据集 —…