[c++高阶]二叉搜索树深度剖析

1.前言

从二叉搜索树开始,后面慢慢学的AVL树,红黑树,map,set,哈希表等等都会慢慢的变得更难了,也更加难以理解了。希望大家能够坚持下去,只要坚持,就是胜利。

 

本章重点

着重讲解什么是二叉搜索树,二叉搜索树的定义、性质以及二叉搜索树的正删查的模拟实现。

2.二叉搜索树的概念 

二叉搜索树:从中序遍历来看就是一颗有序树,即左边比我大,右边比我小。并且左子树也符合这个要求,右子树也符合这个要求。

        也可以称二叉搜索树为二叉查找树,并且他也可以是一棵空树

例:如果这棵树不为空的话

由于左子树中10的右边5比10小,所以这不是一颗二叉搜索树

30的左边15比30小,15作为左子树,没有左右孩子,天然是一棵二叉搜索树,而30的右边全都比30大,且右子树以41为根的左边比其小,右边比其大。所以这棵树是一棵二叉搜索树

3.二叉搜索树的定义及性质

如何定义一棵二叉树呢?

代入如下:

//创建节点
template <class T>
struct BstreeNode
{
	BstreeNode<T>* left;
	BstreeNode<T>* right;
	            T  val;
	//构造函数
	BstreeNode(const T& key)
		:left(nullptr)
		,right(nullptr)
		,val(key)
	{}
};

解释:二叉树由一个一个的节点组成,所以只需要定义出来了节点,再把不同的节点链接起来,那么就组成了一棵二叉树

二叉树的性质:

1.二叉搜索树在用中序遍历时,得到的值就是一个已经排好序的序列了。

例:

用中序遍历可得:1 3 4 6 7 8 10 13 14

2.二叉搜索树只支持增加,删除,查找,不支持修改。

这是因为如果一旦改动了二叉搜索树里面的某一个值,那么这棵二叉搜索树就有可能不是二叉搜索树了,只是一棵普通的二叉树。

3.二叉搜索树的时间复杂度是0(N)

如果二叉树一旦出现最坏的情况,那么就是所有的值都比根小。

例:

由于这个原因,二叉搜索树是不稳定的,所以后续很少用到二叉搜索树,更多的是使用AVL树,和红黑树。

PS:二叉搜索树中是不能够出现相同的值的,如果出现了相同的值,就默认他不是一棵二叉搜索树 

 4.二叉树的模拟实现

 先构造地基,把基本的框架搞出来

//先构造单节点
template <class T>
struct BstreeNode
{
	BstreeNode<T>* left;
	BstreeNode<T>* right;
	            T  val;
	//构造函数
	BstreeNode(const T& key)
		:left(nullptr)
		,right(nullptr)
		,val(key)
	{}
};

//这里模拟实现二叉搜索树
template <class T>
class Bstree
{
public:
	typedef BstreeNode<T> Node;
private:
    Node* _root;
}

4.1 二叉搜索树的查找

例:

要在这一棵二叉搜索树中查找到7,查找代码如下。

迭代版本:

	bool Find(const T& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->val < key)
				cur = cur->right;
			else if (cur->val > key)
				cur = cur->left;
			else if (cur->val == key)
				return true;
		}
		return false;
	}

递归版本:

bool FindR(const T& key)
{
	if (_FindR(_root, key)) return true;
	else return false;
}

bool _FindR(Node* root, const T& key)
{
	if (root == nullptr) return false;
	if (root->val > key) return _FindR(root->left, key);
	else if (root->val < key) return _FindR(root->right, key);
	else return true;
}

简单来说就是总结成一句话:如果当前值比我小,我就去右子树里面找,如果当前值比我大,那我就去左子树里面找。最后的结果要么就是找到了,要么就是这棵二叉搜索树里面根本没有这个值。

4.2 二叉搜索树的插入

1.当树为空的时候,那么直接插入即可

2.如果树不为空,那么就需要先找到是具体插入在哪一个位置的后面,然后再进行插入。

例:

如果要插入一个2

代码如下:

bool Insert(const T& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	//先找到要插入的位置
	Node* prev = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->val > key)
		{
			prev = cur;
			cur = cur->left;
		}
		else if (cur->val < key)
		{
			prev = cur;
			cur = cur->right;
		}
		else return false;
	}

	//到这就找到了要插入的位置
	cur = new Node(key);
	if (prev->val > key)
		prev->left = cur;
	else if (prev->val < key)
		prev->right = cur;
	return true;
}

PS:这里在找的时候,也记录了要插入的位置的前一个节点,这是因为你需要把插入的值链接到前一个节点的左边或者右边,形成一棵二叉搜索树。

 4.3 二叉搜索树的删除

 先判断要删除的节点是否在二叉搜索树里面,如果不在,那就返回。如果在的话,那么就有以下四种情况

1.要删除的节点左为空

2.要删除的节点右为空

3.要删除的节点左右都为空(这种情况包含在1.2两种情况里面)

4.要删除的节点左右都不为空。(这种情况就需要进行特殊处理了,找出左子树的最右值,即最大值,然后与删除的节点值进行交换,那么就转换成了删除这个左子树的最右值所在的节点的位置了。或者找出右子树的最左值,即最小值。)

代码如下:

bool Erase(const T& key)
{
	//1.在删除之前要先找到
	Node* prev = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->val > key)
		{
			prev = cur;
			cur = cur->left;
		}
		else if (cur->val < key)
		{
			prev = cur;
			cur = cur->right;
		}
		else
		{
			//找到了--三种情况--左为空,右为空,左右均不为空
			//1.左为空
			if (cur->left == nullptr)
			{
				//1.先判断是不是根节点
				if (_root == cur)
					_root = cur->right;
				else
				{
					//不是根节点--那么就是链接的问题了
					if (cur == prev->left)
						prev->left = cur->right;
					else if (cur == prev->right)
						prev->right = cur->right;
				}
				delete cur;
				cur = nullptr;
				return true;
			}
			//2.右为空
			else if (cur->right == nullptr)
			{
				//1.先判断是不是根节点
				if (_root == cur)
					_root = cur->left;
				else
				{
					//不是根节点--那么就是链接的问题了
					if (cur == prev->left)
						prev->left = cur->left;
					else if (cur == prev->right)
						prev->right = cur->left;
				}
				delete cur;
				cur = nullptr;
				return true;
			}

			else
			{
				//左右均不为空--那么找左子树的最右,或者右子树的最左来进行替换
				Node* prev = cur;
				Node* minleft = cur->right;
				while (1)
				{
					if (minleft->left != nullptr)
					{
						prev = minleft;
						minleft = minleft->left;
					}
					else
						break;
				}
				//找到了右子树的最左为minleft
				cur->val = minleft->val;
				//还是分两种情况--左为空或者右为空
				if (minleft->left == nullptr)
				{
					if (prev == cur)
						prev->right = minleft->right;
					else
						prev->left = minleft->right;
				}
				else if (minleft->right == nullptr)
				{
					//右为空,那么左右一定是空的,因为minleft就是最左了
					if (prev == cur)
						prev->right = nullptr;
					else
						prev->left = nullptr;
				}
				delete minleft;
				minleft = nullptr;
				return true;
			}
		}
	}
	return false;
}

5.总结

二叉树的模拟实现肯定不只是单单的这么简单,而模拟实现他的目的只是为了更好的理解他的相关操作,以及理解增删查代码的相关写法。我们不是为了造轮子,而是为了更好的理解这个轮子是如何造出来的。

此外:本次只给出来查找函数的迭代写法和递归写法,插入函数和删除函数的递归写法还没有给出,有兴趣的小伙伴参考以下链接。

二叉搜索树的增删改模拟实现 · e87b2ae · 青酒余成/初识数据结构 - Gitee.com

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

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

相关文章

【力扣刷题实战】单值二叉树

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 力扣题目&#xff1a; 单值二叉树 题目描述 示例 1&#xff1a; 示例 2&#xff1a; 解题思路 题目理解 算法选择 具体思路 解题要点 完整代码&#xff08;C语言&#xff09; 兄弟们共勉 &#xff01;&#xff01;…

MySQL数据库MHA高可用

目录 一、MHA简述 二、MHA 的组成 三、MHA 的特点 四、MHA工作原理 五、MHA部署步骤 六、搭建 MySQL MHA MHA一主两从高可用集群示意图 实验环境 1. Master、Slave1、Slave2 节点上安装 mysql5.7 2. 关闭防火墙 3. 修改 Master、Slave1、Slave2 节点的主机名 4. 修…

国内短剧源码短剧系统搭建小程序部署H5、APP打造短剧平台

​在当今的互联网时代&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;受到了越来越多用户的喜爱。为了提供更好的用户体验和满足用户需求&#xff0c;一个好的短剧系统需要具备多元化的功能和优质的界面设计。 本文将介绍国内短剧源码短剧系统搭建小程序部署H5、APP所需的…

【传知代码】图像处理解决种子计数方法

文章目录 一、背景及意义介绍研究背景农业考种需求传统计数方法的局限性人工计数仪器设备计数 研究意义提高育种效率提高计数准确性广泛的适用性数据存档与分析便利 二、概述三、材料与数据准备以及方法介绍整体流程图像采集图像预处理形态学操作腐蚀运算开运算 图像二值化种子…

Typora一款极简Markdown文档编辑器和阅读器,实时预览,序列号生成!免费!最新可用!

文章目录 一、Typora下载和安装二、Typora序列号生成 Typora是一款Markdown编辑器和阅读器&#xff0c;风格极简&#xff0c;实时预览&#xff0c;所见即所得&#xff0c;支持MacOS、Windows、Linux操作系统&#xff0c;有图片和文字、代码块、数学公式、图表、目录大纲、文件管…

C/C++(八)C++11

目录 一、C11的简介 二、万能引用与完美转发 1、万能引用&#xff1a;模板中的 && 引用 2、完美转发&#xff1a;保持万能引用左右值属性的解决方案 三、可变参数模板 1、可变参数模板的基本使用 2、push 系列和 emplace 系列的区别 四、lambda表达式&#xf…

海亮科技亮相第84届中国教装展 尽显生于校园 长于校园教育基因

10月25日&#xff0c;第84届中国教育装备展示会&#xff08;以下简称“教装展”&#xff09;在昆明滇池国际会展中心开幕。作为国内教育装备领域规模最大、影响最广的专业展会&#xff0c;本届教装展以“数字赋能教育&#xff0c;创新引领未来”为主题&#xff0c;为教育领域新…

MYSQL期中复习

MYSQL [语句不要拼错&#xff0c;表名、列名不要写错&#xff0c;语句难记要记住] 创建表 模版 create table 表名(列名1 数据类型 [约束], 列明2 数据类型 [约束], [表级约束]); 约束 单一主码约束 primary key 联合主码约束 primary key(列名1,列名2) [要在列名12定义后…

结合Intel RealSense深度相机和OpenCV来实现语义SLAM系统

结合Intel RealSense深度相机和OpenCV来实现语义SLAM系统是一个非常强大的组合。以下是一个详细的步骤指南&#xff0c;帮助你构建这样一个系统。 硬件准备 Intel RealSense深度相机&#xff1a;例如D415、D435或L515。计算平台&#xff1a;一台具有足够计算能力的计算机&…

无人机之多源信息融合算法篇

一、概述 多源信息融合算法在无人机导航领域中扮演着越来越重要的角色。该算法通过整合来自不同传感器&#xff08;如全球定位系统GPS、惯性导航系统INS、磁力计、气压高度计、视觉传感器等&#xff09;的数据&#xff0c;利用先进的数据融合算法处理这些多源信息&#xff0c;以…

【Spring Boot】元注解

元注解 1.元注解1.1 Target1.2 Retention1.3 Inherited1.4 Documented1.5 interface 2.自定义注解2.1 创建自定义注解类2.2 实现业务逻辑2.3 使用自定义注解 1.元注解 元注解就是定义注解的注解&#xff0c;是 Java 提供的用于定义注解的基本注解。 注解 说明 Retention是注解…

索尔德 APON无线工业轨道机车定位测距仪介绍

索尔德APON无线定位测距仪&#xff0c;简称APON&#xff0c;采用先进的应答式微波测距技术&#xff0c;为车辆赋予了一双敏锐的“智慧之眼”&#xff0c;能够精确捕捉到有轨移动车辆的绝对位置&#xff0c;无论是快速穿梭还是缓慢移动&#xff0c;确保它们能够准确无误地抵达预…

企业如何选择适合自己的智能扭矩系统Torque?_SunTorque

【大家好&#xff0c;我是唐Sun&#xff0c;唐Sun的唐&#xff0c;唐Sun的Sun。一站式数智工厂解决方案服务商】 一、选择适合自己企业的智能扭矩系统时&#xff0c;可以考虑以下几个关键因素&#xff1a; 扭矩精度要求 首先要明确企业生产过程中对扭矩精度的具体要求。如果产…

全面解析:轻松掌握多模态技术精髓

多模态检索 多模态检索是指利用多种数据模态&#xff08;如文本、图像、视频、音频等&#xff09;进行信息检索的技术。它旨在通过整合不同形式的数据&#xff0c;提供更全面、精确和丰富的检索结果&#xff0c;以满足用户多样化的查询需求。 接下来分三部分&#xff1a; 单模…

net 获取本地ip地址,net mvc + net core 两种

net mvc public static string GetIP(HttpRequestBase request){// 尝试获取 X-Forwarded-For 头string result request.Headers["X-Forwarded-For"]?.Split(,).FirstOrDefault()?.Trim();if (string.IsNullOrEmpty(result)){// 获取用户的 IP 地址result reques…

云存储的费用是多少?2024年最新价格表

云存储的费用是多少最新&#xff1f;云存储的费用通常基于多个因素确定&#xff0c;包括存储容量、访问流量、请求次数、服务类型&#xff08;如对象存储、文件存储、块存储等&#xff09;、计费方式&#xff08;按量计费或包年包月&#xff09;以及可能的附加功能&#xff08;…

linux 原子操作

首先是为什么要有 原子操作 网上的截图&#xff1a; 不能从C语言来看&#xff0c;要从汇编来看 但是实际的情况有可能是这样。 A进程没有得到想要的结果。 然后是 原子操作的 底层实现 最终会是这段代码&#xff0c;当然只是一个 加一的操作。 static inline void atomic_a…

从0到1构建 UniApp + Vue3 + TypeScript 移动端跨平台开源脚手架

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f343; vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode&#x1f4ab; Gitee &#x1f…

解析日期、编码

解析日期 这里指的是将字符串或者object类型的日期&#xff0c;转换成panda或python的日期类型。 主要的是dtype的变化&#xff1a;object / str —> datetime64[ns] # modules well use import pandas as pd import numpy as np import seaborn as sns import datetime# …

swiper默认显示三个,中间放大且显示全部图片两边显示部分图片

先上效果图 template <template><div><div class"swiper-content"><div class"swiper-container"><div class"swiper-wrapper"><div class"swiper-slide"><img src"../../assets/images/…