C++-你知道二叉搜索树吗?(循环版)

1.二叉搜索树

1.1 二叉搜索树概念

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

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

 在二叉搜索树中,右子树上的任意一个节点的值都大于当前节点的值,左子树上的任意一个节点的值都小于当前节点的值,所以查找值的时候效率就很高,在任意位置插入和删除数据也不需要挪动,而且搜索二叉树走中序遍历就是一个升序。

1.2 二叉搜索树操作

首先将节点创建出来。

template<class K>
struct BSTreeNode
{
	typedef BSTreeNode<K> Node;
	BSTreeNode(const K& key)
		:_val(key)
		, _left(nullptr)
		,_right(nullptr)
	{}
	Node* _left;
	Node* _right;
	K _key = 0;
};

1.2.1. 二叉搜索树的查找

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

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

查找的逻辑还是比较简单的,就是比较值,大于当前节点的值则继续往右边找,小于当前节点的值则继续往左边找,相等则返回true,如果当前节点是空,说明当前二叉树没有这个值。

	bool find(const K& key)
	{
		Node* cur = root;
		while (cur)
		{
			if (key < cur->_key)
				cur = cur->_left;
			else if (key > cur->_key)
				cur = cur->_right;
			else
				return true;
		}
		return false;
	}

1.2.2. 二叉搜索树的插入

首先我们要判断一下这个二叉搜索树是不是空的,如果是空的就直接new一个节点当成头节点就行。假设要插入一个16,那么需要一个父节点parent来记录上一个节点,因为当cur为空时,需要把16这个节点插入到父节点的左子树或者右子树里面去,那么我们就需要比较大小。因为16大于父节点14,所以插入到父节点的右子树。

	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
				return true;
		}
		cur = new Node(key);
		if (parent->_key < key)
			parent->_right = cur;
		else
			parent->_left = cur;
		return true;
	}

1.2.3. 二叉搜索树的删除

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

情况a:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除

情况b:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

情况c:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除

需要注意的是情况a和b有个例外,就是要删除的节点是根节点,那么另外一颗树就为空,此时需要将根节点更新为另外一颗子树的根。

假设要删除8,那么需要将根节点root更新为8的左节点3.

假设要删除8,那么需要将根节点root更新为8的右节点10.

接下来我来给大家分析一下这三种情况:

情况a:

这种情况就是当前要删除的节点只有左孩子,那么删除之前要保存父节点,然后判断当前节点是父节点的左孩子还是右孩子,删除之后将左孩子成为父节点的左/右孩子。给大家举个例子:

假设我们要删除14这个节点,那么我们只需要判断14是10的左孩子还是右孩子,如果14是左孩子就将13成为10的左孩子,如果14是右孩子,就将13成为10的右孩子。

情况b:

这种情况就是当前要删除的节点只有右孩子,那么删除之前要保存父节点,然后判断当前节点是父节点的左孩子还是右孩子,删除之后将右孩子成为父节点的左/右孩子。给大家举个例子:

假设我们要删除10这个节点,那么我们只需要判断10是8的左孩子还是右孩子,如果10是左孩子就将14成为8的左孩子,如果10是右孩子,就将14成为8的右孩子。

情况c:

这种情况就是当前要删除的节点既有右孩子又有左孩子,那么就需要使用替换法删除。

假设要删除3,那么需要找一个能够替代3的值,那么就需要到右子树找一个最小的值(最右节点),或者去左子树找一个最大的值(最左节点)来替换,因为这样的值替换才能满足二叉搜索树的概念。那么我们去右子树找最小的值,就找到了4.

找到4之后将cur的值3改为4,然后判断4是6的左孩子还是右孩子,因为rightMin不一定是左孩子,比如要删除8,8的rightMin是右孩子10. ,最后删除掉rightMin这个节点。

		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->_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->_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;
						else
							rightMinParent->_right = rightMin->_right;
						delete rightMin;
						return true;
					}
				}
			}
			return false;
		}

1.2.4 二叉搜索树节点个数

		int Size(Node* _root)
		{
			if (_root == nullptr) return 0;
			int lsize = Size(_root->_left);
			int rsize = Size(_root->_right);
			return lsize + rsize + 1;
		}

1.2.5 二叉搜索树叶子节点个数

		int LeafSize(Node* _root)
		{
			if (_root == nullptr) return 0;
			if (_root->_left == nullptr && _root->_right == nullptr)
				return 1;
			return LeafSize(_root->_left) + LeafSize(_root->_right);
		}

今天的分享到这里就结束了,感谢大家的阅读!

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

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

相关文章

AI智能分析网关V4:抽烟/打电话/玩手机行为AI算法及场景应用

抽烟、打电话、玩手机是人们在日常生活中常见的行为&#xff0c;但这些行为在某些场合下可能会带来安全风险。因此&#xff0c;对于这些行为的检测技术及应用就变得尤为重要。今天来给大家介绍一下TSINGSEE青犀AI智能分析网关V4抽烟/打电话/玩手机检测算法及其应用场景。 将监控…

uni-app去除页面头部的标题栏

uniapp项目 每个界面都会有一个标题栏 配置在我们项目根目录的 pages.json中 我们将它全部去掉 上面还是有一条黑的 体验非常差 我们只需要在pages.json中 指定page的 style中加入 "navigationStyle": "custom"对应的page 就没有这个标题栏了

MySQL 核心模块揭秘 | 07 期 | 二阶段提交 (1) prepare 阶段

二阶段提交的 prepare 阶段&#xff0c;binlog 和 InnoDB 各自会有哪些动作&#xff1f; 本文基于 MySQL 8.0.32 源码&#xff0c;存储引擎为 InnoDB。 1. 二阶段提交 二阶段提交&#xff0c;顾名思义&#xff0c;包含两个阶段&#xff0c;它们是&#xff1a; prepare 阶段。…

基础小白快速入门c语言--

变量&#xff1a; 表面理解&#xff1a;在程序运行期间&#xff0c;可以改变数值的数据&#xff0c; 深层次含义&#xff1a;变量实质上代表了一块儿内存区域&#xff0c;我们可以将变量理解为一块儿内存区域的标识&#xff0c;当我们操作变量时&#xff0c;相当于操作了变量…

社会底层人民要被人工智能和机器人淘汰了吗?你害怕被AI代替吗?

随着科技的飞速发展&#xff0c;人工智能和机器人技术已经成为我们日常生活中不可或缺的一部分。这些技术的广泛应用引发了一些担忧&#xff0c;其中之一就是社会底层人民是否会被人工智能和机器人所淘汰。然而&#xff0c;这个问题并不是非黑即白的&#xff0c;它需要从多个角…

IDEA 配置股票插件

IDEA配置股票基金实时查看插件&#xff0c;步骤如下&#xff1a; 打开Settings&#xff0c;找到Plugins&#xff0c;在Marketplace中搜索&#xff1a;Money Never Sleeps&#xff0c;如下图所示&#xff1a; Money Never Sleeps是IntelliJ IDEA平台插件. 支持查看股票实时行情…

Springboot项目中定时任务的四种实现方式

文章目录 1. 使用Scheduled注解1.1 时间间隔执行1.2 固定时间点执行 2. 使用EnableScheduling注解启用定时任务3. 实现SchedulingConfigurer接口4. 使用Quartz框架4.1 配置QuartzScheduler4.2 定义Job类和Trigger类 5. 总结 在开发现代应用时&#xff0c;定时任务是一个非常常见…

模板初阶的补充和string一些函数的用法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 模板初阶的补充 一、C语言中的字符串 二、标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明&#xff08;注意下面我只讲解最常用的接口&…

5个顶级AI训练数据提供商

人工智能革命极大地改变了世界&#xff0c;其影响遍及全球各个行业。 它改变了企业的典型运营方式&#xff0c;从而显着提高了生产力。 大多数公司已经使用或正在考虑某种形式的人工智能。 但为了让机器获得准确的结果&#xff0c;需要可以输入机器学习算法的高质量标记数据。…

大数据可视化python01

import pandas as pd import matplotlib.pyplot as plt# 设置中文改写字体 plt.rcParams[font.sans-serif] [SimHei]# 读取数据 data pd.read_csv(C:/Users/wzf/Desktop/读取数据进行数据可视化练习/实训作业练习/瓜果类单位面积产量.csv ,encoding utf-8)#输出 print(data)…

2024中国5G随身WiFi十大品牌排行榜,20245G随身口碑排行榜,5G随身WiFi2024最新款!5G随身WiFi推荐测评

【中国品牌网中国3C质量评测中心权威榜单联合发布】 第一名&#xff1a;格行5G随身WiFi&#xff1a; 优点&#xff1a;随身WiFi行业的头部和领跑品牌&#xff0c;15年专业物联网行业经验&#xff0c;格行在技术研发、产品创新和客户服务方面具有很高的口碑&#xff0c;被业内…

【Leetcode每日一题】二分查找 - 山脉数组的峰顶索引(难度⭐⭐)(23)

1. 题目解析 Leetcode链接&#xff1a;852. 山脉数组的峰顶索引 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 核心在于找到题目中所说的峰值所在的下标并返回他们的下标即可。 2. 算法原理 峰顶及两侧数据特点分析 峰顶数据…

AI加持下的2023年度威胁态势:一场危机四伏的赛跑

2023年被称为人工智能的元年&#xff0c;大模型技术的持续优化使得AI的处理能力更加强大&#xff0c;AI技术应用已进一步扩展到金融、教育、医疗等行业领域&#xff0c;正逐步渗透到生活的方方面面。数字化与智能化的大势带来的不仅是各领域的变革&#xff0c;回归发展的基础&a…

【单片机学习的准备】

文章目录 前言一、找一个视频是二、画图软件三、装keil5 仿真protues总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 项目需要&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、找一个视频是 https://www.b…

Qt注册类对象单例与单类型区别

1.实现类型SingletonTypeExample #ifndef SINGLETONTYPEEXAMPLE_H #define SINGLETONTYPEEXAMPLE_H#include <QObject>class SingletonTypeExample : public QObject {Q_OBJECT public://只能显示构造类对象explicit SingletonTypeExample(QObject *parent nullptr);//…

通过一篇文章让你了解数据结构和算法的重要性

通过一篇文章让你了解数据结构和算法的重要性 前言一、 什么是数据结构&#xff1f;二、什么是算法&#xff1f;三、数据结构和算法的重要性在校园招聘的笔试中&#xff1a;在校园招聘的面试中&#xff1a;在未来的工作中&#xff1a; 四、如何学好数据结构和算法4.1 死磕代码&…

项目实现json字段

有些很复杂的信息&#xff0c;我们一般会用扩展字段传一个json串&#xff0c;字段一般用text类型存在数据库。mysql5.7以后支持json类型的字段&#xff0c;还可以进行sql查询与修改json内的某个字段的能力。 1.json字段定义 ip_info json DEFAULT NULL COMMENT ip信息, 2.按…

大数据分析案例-基于SVM支持向量机算法构建手机价格分类预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

DDR5内存相比DDR4内存的优势和区别?选择哪一个服务器内存配置能避免丢包和延迟高?

根据幻兽帕鲁服务器的实际案例分析&#xff0c;选择合适的DDR4与DDR5内存大小以避免丢包和延迟高&#xff0c;需要考虑以下几个方面&#xff1a; 性能与延迟&#xff1a;DDR5内存相比DDR4在传输速率、带宽、工作电压等方面都有显著提升&#xff0c;但同时也伴随着更高的延迟。D…

Linux高负载排查最佳实践

在Linux系统中&#xff0c;经常会因为负载过高导致各种性能问题。那么如何进行排查&#xff0c;其实是有迹可循&#xff0c;而且模式固定。 本次就来分享一下&#xff0c;CPU占用过高、磁盘IO占用过高的排查方法。 还是那句话&#xff0c;以最佳实践入手&#xff0c;真传一句话…