当前位置: 首页 > news >正文

数据结构(11)——二叉搜索树

欢迎来到博主的专栏:数据结构
博主ID:代码小豪


文章目录

    • 二叉搜索树
      • 二叉搜索树的声明与定义
      • 二叉搜索树的查找
      • 二叉搜索树的插入
      • 二叉搜索树的中序遍历
      • 二叉搜索树的删除

二叉搜索树

二叉搜索树也称二叉排序树,是具备以下特征的二叉树
(1)每个节点最多拥有两个子节点
(2)对于每个节点,其左子树值均小于根节点,其右子树均大于根节点。

如图:
在这里插入图片描述
该二叉树为二叉搜索树,原因如下:
以节点8为根节点,其左子树均小于8,右子树均大于8.
在这里插入图片描述
以节点3为根节点,其左子树均小于3,其右子树均大于3.
在这里插入图片描述
以此类推。

在这里插入图片描述
该二叉树则不为二叉搜索树,因为以节点7为根节点,节点4小于7,却存在于根节点的右子树,因而不符合右子树均小于根节点这一特征。所以不构成搜索二叉树。

搜索二叉树有个重要的规则就是,在二叉树内不能存在两个相同值的节点,因为这会对二叉搜索树的删除结点造成影响。

二叉搜索树的声明与定义

二叉搜索树可以分为两部分,一部分是节点,另一部分则是树

对于节点,其需要记录一个值,以及分别指向左子节点与右子节点的两个指针。

再设计一个构造函数,通过传递key的值来生成该节点,并将左右指针置为空(方法不唯一,只是博主采用这种构造方式)

template<class k>
struct TreeNode
{TreeNode(const k& key):_key(key),_left(nullptr),_right(nullptr){}k _key;TreeNode* _left;TreeNode* _right;
};

接着是设计一个类来控制整个二叉搜索树,只需要定义一个指向二叉树的根节点的指针成员即可。此外,在设计一些搜索二叉树的成员函数,比如查找节点,排序节点,插入节点和删除结点。

template<class k>
class BSTree
{
public:typedef TreeNode<k> Node;//将节点类型重命名void find(const k& key);//查找void insert(const k& key);//插入节点void erase(const k& key);//删除结点void inorder();//中序遍历
private:Node* _root=nullptr;//根节点
};

二叉搜索树的查找

如何在二叉搜索树当中找到值为key的节点呢?当然,直接使用遍历的形式是可行的,但是利用二叉搜索树的特性可以更加高效。

用key值与根节点进行比较,会出现以下三种情况
(1)key比根节点大
(2)key比根节点小
(3)key等于根节点

如果是情况3,那就说明找到key值了,返回查找的结果

若是情况(1),根据二叉搜索树的特征,比根节点大的值都在右子树,因此进入右子树查找

若是情况(2),比根节点小的值都在左子树,因此进入左子树查找。

如果key来到了空节点处,就说明该二叉搜索树没有匹配的节点,因此查找的结果是无。

bool find(const k& key)//查找
{Node* cur = _root;while (cur != nullptr){if (key > cur->_key)//比key小,进入右树cur = cur->_right;if (key < cur->_key)//比key大,进入左树cur = cur->_left;else//与可以相等,返回结果return true;}return false;
}

动画演示1:假设key为7
在这里插入图片描述
演示动画2:假设key为9
在这里插入图片描述

其实二叉搜索树的查找方式和二分查找的原理非常类似,都是将查找数据的区间一步步缩短,最后确定元素。

二叉搜索树的插入

在二叉搜索树种插入新节点需要解决一个问题
(1)在插入新节点后,如何保持二叉搜索树的性质?

其实也就是搞清楚新节点插入的位置在哪,其实这个问题的解决思路可以参考前面查找节点的方法。

前面提到,通过缩短数据区间,可以在搜索二叉树中定位到元素的位置,我们可以假设待插入的节点已经存在于二叉树当中。通过查找该节点的方式,定位到元素的位置,然后将该节点插入到对应的位置即可。

思路如下:
(1)通过查找的方式缩短区间(即用key对比根节点)
(2)如果走到了空节点,就说明定位到了元素的位置,直接插入即可
(3)如果存在与插入节点相同值的节点,根据二叉搜索树不能存在两个相同节点的特性,停止不合法的插入操作

还存在一种特殊情况,即当二叉搜索树为空树时,此时无法通过查找的方式进行搜索(因为要让key和根节点的值进行比较,但是空树没有根节点),解决方法是直接生成根节点即可。

bool insert(const k& key)//插入节点
{if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* parent = nullptr;while (cur != nullptr){if (key > cur->_key)//比key小,进入右树{parent = cur;cur = cur->_right;}else if (key < cur->_key)//比key大,进入左树{parent = cur;cur = cur->_left;}else//与key相等,不合法的插入操作return false;}//来到空节点处,将新节点插入至该处cur = new Node(key);if (key > parent->_key)parent->_right = cur;elseparent->_left = cur;return true;
}

动画演示:
在这里插入图片描述

二叉搜索树的中序遍历

中序遍历二叉搜索树,会得到一个升序的结果,先来写出代码,再分析原理。

void inorder()//中序遍历
{_inorder(_root);
}
void _inorder(Node* root)
{if (root == nullptr)return;_inorder(root->_left);cout << root->_key<<' ';_inorder(root->_right);
}

执行如下操作:

int arr[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
BSTree<int> b;
for (auto e : arr)
{b.insert(e);
}

上述代码的作用是构造出下图的二叉搜索树
在这里插入图片描述
再中序遍历该二叉搜索树

b.inorder();

运行结果为:
在这里插入图片描述
那么为什么中序遍历会呈现出升序的特征呢?

根据二叉搜索树的定义,对于每个根节点来说,其左子树小于根节点,其右子树大于根节点。那么下面这个二叉树的排序为
在这里插入图片描述
Ln2<n1<Rn2

在这里插入图片描述
Ln3<Ln2<Rn3<n1<Ln4<Rn2<Rn4.

把小于号去掉,即为
Ln3 Ln2 Rn3 n1 Ln4 Rn2 Rn4.
这个顺序既符合中序遍历的顺序,也符合升序的顺序。我们也可以由此得出一个结论

离根节点越左的节点,越小,离根节点越右的节点,越大,越靠近根节点的节点,越接近根节点。比如上图中的Ln4和Rn3.
在这里插入图片描述
这个结论将会在二叉搜索树的删除中起到非常大的左右。

二叉搜索树的删除

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

a. 要删除的结点无子结点
在这里插入图片描述
解决方法:直接删除即可。

b. 要删除的结点只有左子结点
在这里插入图片描述
删除该结点且使被删除节点的父结点指向被删除节点的左子结点
在这里插入图片描述

c. 要删除的结点只有右子结点
在这里插入图片描述
删除该结点且使被删除节点的父结点指向被删除结点的右子结点
在这里插入图片描述
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来。

d. 要删除的结点有左、右孩子结点
在这里插入图片描述

这种情况下是最麻烦的,因为我们不能贸然的将节点3删除,这会断开二叉搜索树与节点3的左右子树的链接,破坏结构。

换句话说,要删除节点3,就要找到一个节点来替换节点3,这样才能保持住二叉搜索树的特性。那么什么节点可以胜任呢?

为了维持二叉搜索树的特性,因此替换节点的节点应该有以下特征:(1)比左子节点大(2)比右子节点小。因此选取来替换的节点应该越接近待删除的节点越好。
在这里插入图片描述
在上一节中提到,越接近根节点的节点,其值也越接近根节点。对于待删除的节点来说,最接近它的值无非两个
(1)其左子树最右边的节点
(2)其右子树最左边的节点
这两个节点都是离待删除节点最近的节点。也是值最接近的节点。因此,选取这两个节点来替换待删除的节点再合适不过了。

删除操作的实现如下:

bool erase(const k& key)//删除结点{Node* cur = _root;Node* parent = cur;while (cur != nullptr){if (key > cur->_key)//比key小,进入右树{parent = cur;cur = cur->_right;}if (key < cur->_key)//比key大,进入左树{parent = cur;cur = cur->_left;}else//与key相等,删除该节点{if (cur->_left == nullptr)//情况a、bif (parent->_left = cur)parent->_left = cur->_right;elseparent->_right = cur->_right;else if(cur->_right==nullptr)//情况cif (parent->_left = cur)parent->_left = cur->_left;elseparent->_right = cur->_left;else//情况d{Node* RightMinp = cur;//右子树的最左节点的父节点Node* RightMin = cur->_right;//右子树的最左节点while (RightMin->_left != nullptr){RightMinp = RightMin;RightMin = RightMin->_left;}cur->_key = RightMin->_key;//交换节点if (RightMinp->_right == RightMin)RightMinp->_right = RightMin->_right;elseRightMinp->_left = RightMin->_right;delete RightMin;}return true;}}return false;}

相关文章:

数据结构(11)——二叉搜索树

欢迎来到博主的专栏&#xff1a;数据结构 博主ID:代码小豪 文章目录 二叉搜索树二叉搜索树的声明与定义二叉搜索树的查找二叉搜索树的插入二叉搜索树的中序遍历二叉搜索树的删除 二叉搜索树 二叉搜索树也称二叉排序树&#xff0c;是具备以下特征的二叉树 &#xff08;1&#x…...

如何使用和配置 AWS CLI 环境变量?

欢迎来到雲闪世界。环境变量在配置和保护应用程序方面起着至关重要的作用&#xff0c;在使用 AWS CLI&#xff08;命令行界面&#xff09;时&#xff0c;它们的使用尤其重要。在这篇博客文章中&#xff0c;我们将深入探讨环境变量的世界&#xff0c;探索它们的用途、它们在 AWS…...

七、流程控制

if语句 在go语言中if语句的写法是比较简单的&#xff0c;也是很常见的 func main() {a : trueif a {fmt.Println("a is true")} }if else 语句 func main() {a : trueif !a {fmt.Println("a is true")} else {fmt.Println("a is false")} }el…...

【通过python启动指定的文件】

通过python启动指定的文件 在 Python 中&#xff0c;可以使用os模块的startfile函数&#xff08;在 Windows 系统中&#xff09;或者subprocess模块来启动指定的文件。 以下是使用os模块在 Windows 系统中的示例&#xff1a; import osfile_path "C:\\path\\to\\your\…...

区块链开源的项目有哪些?

区块链领域有许多开源项目&#xff0c;它们覆盖了从基础设施到应用层的不同方面。以下是一些著名的区块链开源项目&#xff1a; 1. Bitcoin (比特币)&#xff1a;第一个去中心化的加密货币&#xff0c;源代码在 GitHub 上开源。它实现了区块链技术的基本概念。 2. Ethereum (…...

3152. 特殊数组 II(24.8.14)

题目 如果数组的每一对相邻元素都是两个奇偶性不同的数字&#xff0c;则该数组被认为是一个 特殊数组 。 你有一个整数数组 nums 和一个二维整数矩阵 queries&#xff0c;对于 queries[i] [fromi, toi]&#xff0c;请你帮助你检查 子数组 nums[fromi…toi] 是不是一个 特殊数组…...

Android 全系统版本文件读写最佳适配,CV 即用(适配到 Android 14)

结合着Android的历史问题&#xff0c;我们需要这样写才行&#xff1a; 首先 manifest 部分 <manifest><!-- Devices running Android 12L (API level 32) or lower --><uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE" a…...

【日记】朋友和他女朋友领证了(368 字)

正文 一定程度上感受到了驻场运维的水深火热&#xff0c;感觉成天到晚都在救火。今天下午就给人修了四五台机器…… 回想了一下&#xff0c;今天貌似还真没干什么。毕竟早上睁眼就是 8:35 了&#xff0c;给人吓得半死。 &#xff08;感觉 AI 也很智障&#xff0c;当初就是发现音…...

行业大模型:信用评分大模型、生产优化大模型、库存管理大模型、物流行业大模型、零售行业大模型

金融行业大模型&#xff1a;信用评分大模型 信用评分模型在金融行业中扮演着至关重要的角色&#xff0c;它通过对个人或企业的信用状况进行评估&#xff0c;帮助金融机构有效控制风险&#xff0c;提高业务效率。以下是信用评分模型的特点及案例介绍&#xff1a; 信用评分模型…...

VSCode 搭配 Windows 下各种 C/C++ 编译器使用

Visual Studio Code(简称 VSCode)是一款由微软开发的免费、开源的代码编辑器,它支持多种编程语言,包括 C 和 C++。VSCode 提供了丰富的扩展和定制功能,使得开发者能够根据自己的需求进行个性化设置。在 Windows 环境下,搭配合适的 C/C++ 编译器,VSCode 能够成为一个强大…...

【JavaEE】线程池和定时器

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 ✏️一.线程池 在Java中&#xff0c;线程池&#xff08;Thread Pool&#xff09;是一种用于管理并发线程的机制&#xff0c;它提供了一种创建、复用和管理一组…...

《Unity3D网络游戏实战》通用服务器框架

服务端程序的两大核心是处理客户端的消息和存储玩家数据 模块划分 游戏流程 连接阶段&#xff1a;客户端调用Connect连接服务端即为连接阶段。连接后双端即可通信&#xff0c;但服务端还不知道玩家控制的是哪个角色。于是客户端需要发送一条登录协议&#xff0c;协议中包含用户…...

LeetCode404 左叶子之和

前言 题目&#xff1a; 404. 左叶子之和 文档&#xff1a; 代码随想录——左叶子之和 编程语言&#xff1a; C 解题状态&#xff1a; 成功解答&#xff01; 思路 注意左叶子节点的定义&#xff1a;节点A的左孩子不为空&#xff0c;且左孩子的左右孩子都为空&#xff08;说明是…...

nodejs操作redis的工具类

const Redis require("ioredis");async function generateStreamID() {// 生成时间戳&#xff08;毫秒级&#xff09;const timestamp Date.now();// 生成唯一的序列号const sequenceNumber Math.random() * 1000; // 根据需要生成唯一的序列号// 构建 Stream ID&…...

关于wsl2与win11互联互通的问题

首先搞清楚使用场景。我是在win11上写go做后端api&#xff0c;在WSL2 的Linux上写前端页面。 我发现在windows 里写go语言没啥问题&#xff0c;我的后端api部署在win11上。但是在win11上写前端经常会遇到莫名其妙的故障&#xff0c;一会npm包下不来一会说包之间的依赖结构出问题…...

C++ 类型转换

目录 0.前言 1.C语言类型转换 1.1隐式类型转换 1.2显式类型转换 2.C强制类型转换 2.1 static_cast 2.2 reinterpret_cast 2.3 const_cast 2.4 dynamic_cast 3.为什么C需要4种强制类型转换 3.1类型转换的多样性需求 3.2提高类型转换的安全性 3.3提供更明确的语义 3.4支持高级编程…...

2024挖漏洞给报酬的网站汇总,兼职副业3天收益2k

文章目录 一、众测平台(国内)二、前沿漏洞研究奖励计划三、行业SRC四、企业应急响应中心-SRC-汇总 1、互联网企业2、生活服务、住宿、购物相关企业3、物流、出行、旅游4、金融相关企业5、视频游戏直播社交娱乐6、教育、问答、知识付费7、泛科技通讯物联网云服务8、安全企业9、其…...

0到1学习Google广告(2):掌握展示位置及排名规则丨出海笔记

大家好, 我是专注谷歌广告和谷歌SEO的谷哥哥哥&#xff0c;感谢出海笔记Alan邀请。今天我们来聊聊广告界的大拿——谷歌广告。在这个数字营销的黄金时代&#xff0c;无论是B2B、B2C还是品牌类客户&#xff0c;谷歌广告都是一个不容忽视的战场。那么&#xff0c;如何在这个战场上…...

MySQL数据库读超时/SELECT查询超时 杂记

本文环境 阿里云RDS MySQL 8.0.34 当客户端向MySQL数据库发送一条SQL之后,由于SQL很慢很慢,它会在什么时候结束呢? 查看 max_execution_time 变量值 mysql> show variables like max_execution_time; --------------------------- | Variable_name | Value | ------…...

docker数据卷:

docker数据卷&#xff1a; 容器和宿主机之间数据共享 容器和宿主机之间数据共享——————挂载卷————容器内的目录和宿主机的目录进行挂载&#xff0c;实现数据文件共享 容器的生命周期有限&#xff0c;一旦重启所有对容器内部文件数据的修改以及保存的数据都会被初始…...

西门子S7-300PLC与组态王技术结合的混凝土搅拌站智能配料系统研究

110#西门子S7-300PLC和组态王的混凝土搅拌站配料系统老司机带你拆解混凝土搅拌站的自动化配料系统&#xff0c;今天咱们聊聊西门子S7-300PLC和组态王的黄金组合。这个系统就像混凝土界的米其林大厨&#xff0c;精确到克的配方控制才是核心竞争力。先看PLC这边的硬核操作。配料皮…...

一个企业申请VPC上的IP网段,和私有部署,最多独立可以容纳多少台主机

一个 VPC 能容纳的主机数量&#xff0c;取决于你问的是“理论最大值”还是“实际可用值”。 简单来说&#xff1a;理论上一张网能放得下 1677 万台主机&#xff0c;但在阿里云上&#xff0c;为了保证网络稳定&#xff0c;一个 VPC 实际最多能让你用 30 万个私网地址。&#x1f…...

【C++面经】轻舟智航自动驾驶应用软件开发实习岗位

一面&#xff1a; 1、项目相关 (1)介绍一下你的多线程模型以及线程之间是怎麽通信的&#xff1b; (2)“消息风暴”是什么怎麽造成的 (3)关于机器人项目的串口协议是怎么自定义的 2、智能指针讲一下 3、Malloc和new的区别&#xff08;底层实现也说一下&#xff09; 能不能对mall…...

避坑指南:Windows搭建Turn服务器常见问题及解决方案

Windows平台Turn服务器部署避坑实战手册 在实时音视频通信领域&#xff0c;Turn服务器扮演着关键的中继角色&#xff0c;特别是在NAT穿透场景中。Windows平台因其广泛的用户基础&#xff0c;成为不少开发团队的首选部署环境。然而&#xff0c;从源码编译到服务配置的每一步都可…...

TanStack Virtual 终极性能优化指南:10个实用技巧让大型列表流畅如飞

TanStack Virtual 终极性能优化指南&#xff1a;10个实用技巧让大型列表流畅如飞 【免费下载链接】virtual 项目地址: https://gitcode.com/gh_mirrors/virtu/virtual TanStack Virtual 是一个强大的虚拟列表库&#xff0c;能够帮助开发者在处理大型数据列表时保持 60F…...

AI头像生成器开发者案例:集成至内部AI绘图平台的API对接实践

AI头像生成器开发者案例&#xff1a;集成至内部AI绘图平台的API对接实践 1. 引言&#xff1a;从独立工具到平台核心组件 如果你正在开发一个AI绘图平台&#xff0c;或者运营一个需要大量创意头像的社区&#xff0c;你可能会遇到这样的问题&#xff1a;用户有想法&#xff0c;…...

RakNet多平台部署实战:Windows、Linux、Mac、iOS和Android全攻略

RakNet多平台部署实战&#xff1a;Windows、Linux、Mac、iOS和Android全攻略 【免费下载链接】RakNet RakNet is a cross platform, open source, C networking engine for game programmers. 项目地址: https://gitcode.com/gh_mirrors/ra/RakNet RakNet是一款跨平台、…...

555时基芯片压控振荡器的非线性特性分析与超声波调制应用

1. 555时基芯片压控振荡器基础原理 555时基芯片可以说是电子工程师的"瑞士军刀"&#xff0c;从简单的闪光灯到复杂的PWM控制器都能见到它的身影。我第一次接触555芯片是在大学电子实验课上&#xff0c;当时用它做了一个LED闪烁电路&#xff0c;没想到这个小小的芯片还…...

CasRel关系抽取模型案例集:微博短文本中‘用户-提及-话题’实时关系流抽取

CasRel关系抽取模型案例集&#xff1a;微博短文本中‘用户-提及-话题’实时关系流抽取 1. 引言&#xff1a;短文本中的关系挖掘挑战 你有没有刷过微博&#xff0c;看到一条热门微博下面成千上万的评论和转发&#xff0c;里面充满了各种和#话题标签&#xff1f;这些看似杂乱无…...

STM32F401RE HSI+PLL 84MHz轻量时钟配置库

1. 项目概述ST_401_84MHZ是一个面向 STM32F401RE Nucleo 开发板的轻量级时钟配置库&#xff0c;其核心目标是将系统主频&#xff08;SYSCLK&#xff09;稳定、可靠地提升至84 MHz。该频率并非芯片默认出厂配置&#xff08;F401RE 的默认 HSI 为 16 MHz&#xff0c;复位后 SYSCL…...