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

Cpp二叉搜索树的讲解与实现(21)

文章目录

  • 前言
  • 一、二叉搜索树的概念
    • 定义
    • 特点
  • 二、二叉树的实现
    • 基本框架
    • 查找
    • 插入
    • 删除
      • 当只有0 ~ 1个孩子的时候
      • 当有2个孩子的时候
  • 三、二叉树的应用
    • K模型
    • KV模型
  • 四、二叉树的性能分析
  • 总结


前言

这是全新的一个篇章呢,二叉搜索树是我们接下来学习set、map的前提
迈过它吧,关关难过关关过!

正文开始!


一、二叉搜索树的概念

定义

  二叉搜索树(Binary search tree)是基于二叉树的一种改进版本。因为 普通二叉树没有实际价值,无法进行插入、删除等操作(无意义),但二叉搜索树就不一样了,二叉搜索树对于数据的存储有严格要求:左节点比根小,右节点比根大

因此 二叉搜索树 的查找效率极高,具有一定的实际价值

  所以将数据存入 二叉搜索树 中进行查找时,理想情况下只需要花费 logN 的时间(二分思想)

  这就是 二叉搜索树 名字的由来,搜索(查找)速度很快

特点

  二叉树的基本特点:左比根小,右比根大

  1. 若某个节点的 左 节点不为空,则 左 节点的值一定比当前节点的值 小,且其 左 子树的所有节点都比它 小
  2. 若某个节点的 右 节点不为空,则 右 节点的值一定比当前节点的值 大,且其 右 子树的所有节点都比它 大
  3. 二叉搜索树的每一个节点的 根、左 、右 都满足基本特点

另外,中序遍历的结果为升序

二、二叉树的实现

二叉搜索树的源代码

基本框架

  跟普通的二叉树一样,二叉搜索树也需要节点类,同时将节点指针作为二叉搜索树的成员变量

template <class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key = K()):_key(key),_left(nullptr),_right(nullptr){}};template <class K>
class BSTree
{typedef BSTNode<K> Node;
public:BSTree():_root(nullptr){}private:Node* _root;
};

查找

  得益于二叉搜索树的特性,我们可以比较插入数字和当前节点的值,当比当前节点大的时候的时候往右走,反之则往左,若 cur 为空,那么返回 false ,若找到,则返回 true
在这里插入图片描述

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

插入

  插入其实过程和查找差不多,只不过如果中途找到了就返回 false 表示二叉搜索树中已经有该数字,如果成功走到空了就开始插入
在这里插入图片描述

  只不过我们需要注意,当搜索树为空树的时候,我们必须新建立一个节点,将指针赋给这个根节点,另外,我们需要申请一个指针变量 parent 来记录父节点,方便后续链接

bool Insert(const K& key)
{// 如果为空树,则直接建立一个节点// 将其地址存放在_root上if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;// 一直循环到cur为空while (cur) {if (key > cur->_key) {parent = cur;cur = cur->_right;}else if (key < cur->_key) {parent = cur;cur = cur->_left;}else {// 如果中途发现BS树已有key,则插入失败// BS树中没有重复元素,依据定义return false;}}// 此时建立一个节点,将其地址赋值给curcur = new Node(key);// 此时需要根据值的大小来判断parent左链还是右链if (key > parent->_key)parent->_right = cur;else parent->_left = cur;return true;
}

删除

  删除可就复杂了,要考虑很多情况!

  我们发现如果我们要删除一个节点,并且在二叉搜索树中已经确定找到了该节点,可能有三种情况:

  1. 该节点没有孩子,即要删除的是叶子节点
  2. 该节点只有一个孩子,可能是左孩子为空,也有可能是有孩子为空
  3. 该节点有两个孩子,这种情况比较复杂,要考虑比较复杂的情况

当只有0 ~ 1个孩子的时候

  我们先来看第二种情况,当找到要删除的节点且该节点只有一个孩子后,此时显然父节点链上当前节点的子节点就可以了,这样不会破坏二叉搜索树的结构

二叉搜索树的右子树的值一定大于该节点的值,同样的,左子树的值一定小于该节点的值

  于是,我们就想着再销毁当前节点之前,先判断是父节点的左边链接还是右边链接,这很简单,我们检查一下 parent 左右指针哪个指向 cur 就行,同时,我们也要思考一下子节点与 cur 的链接关系,很简单,这也是直接判断一下就可以

其实,这种方法囊括了第一第二种情况,你可以思考一下为什么

// 如果左孩子为空
// 这时候就要parent就要链到cur的右边去
if (cur->_left == nullptr) {if (parent->_left == cur)parent->_left = cur->_right;else parent->_right = cur->_right;// 删除delete cur;cur = nullptr;return true;
}// 如果右孩子为空
// 这时候就要parent就要链到cur的左边去
else if (cur->_right == nullptr) {if (parent->_left == cur)parent->_left = cur->_left;else parent->_right = cur->_left;// 删除delete cur;cur = nullptr;return true;
}

右子树为空
在这里插入图片描述

左子树为空
在这里插入图片描述

当有2个孩子的时候

在这里插入图片描述

  当左右孩子节点都不为空的时候,我们也要想想,万一把 cur 给删掉了,要换那一个替上来?

  关于这个问题,我们还是要把握二叉搜索树的一个核心特性,就是左子树所有节点的值一定比根节点小,右子树所有节点的值一定比根节点大

  那么只要将左子树最大的值和右子树最小的值找到,此时我们又要想,将两个其中之一的值替代父节点的值即可,然后再销毁节点,那么这样会不会破坏二叉树的结构呢?

  显然不会,只要能正确销毁并正确链接,那么就没关系,在这里我们选择找到右子树的最小值,这很简单,因为一个二叉搜索树的最小值就是最左边那个,那么我们同样用 rightMin 标记右子树的最小节点 ,用 rightMinP 标记其父节点,又为了防止 rightMinP 进不去循环,我们给 rightMinP 赋值 cur


Node* rightMinP = cur;
Node* rightMin = cur->_right;// 找到右子树的最小节点
while (rightMin->_left) {rightMinP = rightMin;rightMin = rightMin->_left;
}// 替代,这时候转换成就要删除rightMin这个节点了
// 这个时候就需要有它的父亲
cur->_key = rightMin->_key;// 因为rightMin必然是最左节点
// 所以rightMinP必然是链接rightMin的右孩子
// 同时rightMinP是左链还是右链这不确定,需要判断一下
if (rightMinP->_left == rightMin)rightMinP->_left = rightMin->_right;
else rightMinP->_right = rightMin->_right;delete rightMin;
rightMin = nullptr;return true;

  但是但是!!我们发现写到这里后,当删到最后只剩几个节点之后,报错了!

  我们再回看代码,发现我们的逻辑是先找到删除节点,再用父亲节点去链接当前节点的子节点,关键是,有没有可能一开始我们就找到了要删除的节点,父亲节点没进循环,也就是说,没有父亲节点,这很不好,针对这种情况,我们就要移动根节点

if (parent == nullptr) {_root = _root->_right;delete cur;cur = nullptr;return true;
}

三、二叉树的应用

K模型

  K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值

我们上述代码实现的也就是这种

  举个例子,给一个单词word,判断该单词是否拼写正确,具体方式如下:

  1. 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  2. 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误

KV模型

  每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对

  比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对

  再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对

KV模型也就是在K模型的基础上加上value的值,请试试吧!

四、二叉树的性能分析

  如果我问你二叉搜索树的查找时间复杂度为多少,你可能会不假思索的回答出是O(logN),但是,假如我给一个递减数列呢,是不是就退化成单支树了?

  所以理想状态下是O(logN),最坏情况下是O(N)


总结

  看了性能分析,你可能会想怎么让二叉树的性能达到最优?不急,AVL树和红黑树已经在路上了!~

相关文章:

Cpp二叉搜索树的讲解与实现(21)

文章目录 前言一、二叉搜索树的概念定义特点 二、二叉树的实现基本框架查找插入删除当只有0 ~ 1个孩子的时候当有2个孩子的时候 三、二叉树的应用K模型KV模型 四、二叉树的性能分析总结 前言 这是全新的一个篇章呢&#xff0c;二叉搜索树是我们接下来学习set、map的前提 迈过它…...

微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)

微服务设计模式 — 补偿事务模式&#xff08;Compensating Transaction Pattern&#xff09; 定义 在云计算和分布式系统中&#xff0c;管理跨多个微服务或组件的事务一致性是一项极具挑战性的任务&#xff0c;补偿事务模式Compensating Transaction Pattern&#xff09;是一种…...

20 实战:形状编码、运动补偿和纹理编码的实现(基于python)

在当今多媒体时代,视频处理与编码已经成为各个领域中不可或缺的一部分。无论是视频编辑、流媒体传输,还是计算机视觉应用,视频编码技术都扮演着关键角色。本文将详细解析一个基于Python的图形用户界面(GUI)视频编码器。通过对代码的逐行讲解、功能分析以及参数调节方法的探…...

区块链-C++挖矿软件XMRIG源码分析

C++挖矿软件源码分析 3rdpartybackendgrgon2Obfusheader.hmain 程序 xmrig.cppxmrig命名空间process类Entry::IdApp类CoreControllerbasetoolkernelinterfacesDonateStrategy.cppdonate.h/2/dmiCmake 跨平台的自动化构建系统CMakeLists.txt.cmake 13个引入算力哈希率 HashrateE…...

C语言指针的介绍

零.导言 在日常生活中&#xff0c;我们常常在外出时居住酒店&#xff0c;细心的你一定能发现酒店不同的房间上有着不同的门牌号&#xff0c;上面写着像308&#xff0c;512之类的数字。当你定了酒店之后&#xff0c;你就会拿到一个写有门牌号的钥匙&#xff0c;凭着钥匙就能进入…...

八大排序算法——堆排序

目录 前言 一、向上调整算法建堆 二、向下调整算法建堆 三、堆排序 前言 堆排序是基于堆结构的一种排序思想&#xff0c;因此要为一个乱序的数组进行排序的前提是数组必须要是一个堆&#xff0c;所以要先对数组进行建堆操作 一、向上调整算法建堆 时间复杂度&#xff1a;O…...

U盘文件不翼而飞?这些数据恢复工具帮你找回!

U盘因其便携性是我们日常工作和生活中不可或缺的工具。不过有时候它也会出点小状况。如果你U盘里的数据突然不见了&#xff0c;不要着急&#xff0c;可以先试试这几款数据恢复工具&#xff01; 福昕数据恢复 直达链接&#xff1a;www.pdf365.cn/foxit-restore/ 操作教程&…...

在Java中 try catch 会影响性能吗?

1、在Java中&#xff0c;异常处理确实会对性能产生影响&#xff0c;但在正常执行的代码路径中&#xff0c;即没有发生异常的情况下&#xff0c;try-catch块的性能影响是微不足道的 2、但是&#xff0c;如果出现异常被抛出时&#xff0c;Java虚拟机需要执行一些额外的操作来处理…...

吞吐量最高飙升20倍!破解强化学习训练部署难题

**强化学习&#xff08;RL&#xff09;对大模型复杂推理能力提升有关键作用&#xff0c;然而&#xff0c;RL 复杂的计算流程以及现有系统局限性&#xff0c;也给训练和部署带来了挑战。近日&#xff0c;字节跳动豆包大模型团队与香港大学联合提出 HybridFlow&#xff08;开源项…...

redis的数据过期策略

Redis对数据设置了数据的有效时间,数据过期之后,就需要将数据从内存中删除掉.可以按照不同的规则进行删除,这种删除规则就被称之为数据的删除策略(数据过期策略),而这种策略有两种:惰性删除和定期删除 惰性删除:设置key过期时间后,我们不去管它,当需要该key时,我们在检查其是否…...

三周精通FastAPI:27 使用使用SQLModel操作SQL (关系型) 数据库

官网文档&#xff1a;https://fastapi.tiangolo.com/zh/tutorial/sql-databases/ SQL (关系型) 数据库 FastAPI不需要你使用SQL(关系型)数据库。 但是您可以使用任何您想要的关系型数据库。 这里我们将看到一个使用SQLModel的示例。 SQLModel是在SQLAlchemy和Pydantic的基础…...

Kubernetes金丝雀发布

华子目录 Canary金丝雀发布什么是金丝雀发布Canary发布方式基于header&#xff08;http包头&#xff09;灰度发布基于权重的金丝雀发布 Canary金丝雀发布 什么是金丝雀发布 金丝雀发布也称为灰度发布&#xff0c;是一种软件发布策略主要目的是在将新版本的软件全面推广到生产环…...

树形DP讲解

文章目录 树形DP讲解一、引言二、树形DP基础1、树的定义2、树形DP的基本思想3、代码示例&#xff1a;子树大小 三、经典例题解析1、树的平衡点1.1、代码示例 2、没有上司的舞会&#xff08;树的最大独立集&#xff09;2.1、代码示例 四、总结 树形DP讲解 一、引言 树形动态规…...

容器:如何调试容器

调试容器&#xff0c;主要是指的调试Dockerfile&#xff0c;调试Dockerfile中的各个命令的执行&#xff0c;大小等 1、docker history查看构建过程和所有的中间层 2、docker run rm -it -u root XXX sh&#xff0c;通过临时容器的方式启动&#xff0c;可以调试中间层文件 3、do…...

用图说明 CPU、MCU、MPU、SoC 的区别

CPU CPU 负责执行构成计算机程序的指令&#xff0c;执行这些指令所指定的算术、逻辑、控制和输入/输出&#xff08;I/O&#xff09;操作。 MCU (microcontroller unit) 不同的 MCU 架构如下&#xff0c;注意这里的 MPU 表示 memory protection unit MPU (microprocessor un…...

牛客周赛 Round 65

文章目录 超市思路&#xff1a;Solved&#xff1a; 雨幕思路&#xff1a;Solved&#xff1a; 闺蜜思路&#xff1a;Solved&#xff1a; 医生思路&#xff1a;Solved&#xff1a; 降温&#xff08;easy&#xff09;思路&#xff1a;Solved&#xff1a; F-降温&#xff08;hard&a…...

超级经典的79个软件测试面试题(内含答案)

1、软件的生命周期(prdctrm) 计划阶段(planning)-〉需求分析(requirement)-〉设计阶段(design)-〉编码(coding)->测试(testing)->运行与维护(running maintrnacne) 测试用例 用例编号 测试项目 测试标题 重要级别 预置条件 输入数据 执行步骤 预期结果 2、问&#xf…...

【Mac】安装 F5-TTS

1、下载项目 项目地址&#xff1a;【GitHub】 SWivid F5-TTS 2、创建并激活 Python 虚拟环境 # 创建 Python 虚拟环境 userMac F5-TTS-main % python3 -m venv f5-tts# 激活进入 Python 虚拟环境 userMac F5-TTS-main % source f5-tts/bin/activate (f5-tts) userrMac F5-TT…...

Leaflet查询矢量瓦片偏移的问题

1、问题现象 使用Leaflet绘制工具查询出来的结果有偏移 2、问题排查 1&#xff09;Leaflet中latLngToContainerPoint和latLngToLayerPoint的区别 2&#xff09;使用Leaflet查询需要使用像素坐标 3&#xff09;经排查发现&#xff0c;container获取的坐标是地图容器坐标&…...

存储引擎技术进化

B-tree 目前支撑着数据库产业的半壁江山。 50 年来不变而且人们还没有改变它的意向 鉴定一个算法的优劣&#xff0c;有一个学派叫 IO复杂度分析 &#xff0c;简单推演真假便知。 下面就用此法分析下 B-tree(traditional b-tree) 的 IO 复杂度&#xff0c;对读、写 IO 一目了…...

开源协议解析与合规实践指南

1. 开源协议的本质与重要性作为一名在软件行业摸爬滚打十多年的开发者&#xff0c;我见过太多因为忽视开源协议而引发的纠纷案例。记得2018年某创业团队就因误用GPL协议代码导致整个产品被迫开源&#xff0c;最终项目流产。开源协议绝非一纸空文&#xff0c;它直接关系到开发者…...

NVIDIA Profile Inspector深度解析:解锁显卡隐藏性能与高级配置实战指南

NVIDIA Profile Inspector深度解析&#xff1a;解锁显卡隐藏性能与高级配置实战指南 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector NVIDIA Profile Inspector是一款面向技术爱好者和开发者的专业显卡配…...

C++27执行策略演进全图谱(从C++17到C++27 TS23742的5次关键修订与ABI兼容性断崖预警)

第一章&#xff1a;C27执行策略的范式跃迁与标准定位C27正将执行策略&#xff08;Execution Policies&#xff09;从“并行化提示”升格为“可验证执行契约”&#xff0c;标志着标准库算法语义模型的根本性重构。这一转变不再仅依赖实现对std::execution::par_unseq等策略的启发…...

SoftSPIB:支持任意位宽的软件模拟SPI库

1. SoftSPIB&#xff1a;面向非字节对齐SPI通信的软件实现方案1.1 问题起源&#xff1a;硬件SPI的固有局限性在嵌入式系统开发中&#xff0c;SPI&#xff08;Serial Peripheral Interface&#xff09;作为最常用的同步串行总线协议&#xff0c;其标准实现通常以8位&#xff08;…...

轻量级嵌入式软传感库:用双BME280实现太阳辐射实时反演

1. 项目概述FiaPhy 是一个面向嵌入式环境的轻量级软传感&#xff08;Soft-Sensing&#xff09;库&#xff0c;核心实现差分时间导数软传感&#xff08;Differential Temporal Derivative Soft-Sensing, DTDSS&#xff09;算法。该库不依赖专用辐射计硬件&#xff0c;而是通过部…...

计算机毕业设计springboot展会门票系统 基于SpringBoot的会展票务数字化服务平台 SpringBoot框架下的博览会入场券预约与核销系统

计算机毕业设计springboot展会门票系统 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着会展经济的蓬勃发展和数字化转型的深入推进&#xff0c;各类展会活动规模不断扩大&am…...

CDA Level-2 考试全攻略:从报名到备考的保姆级教程(含最新题库资源)

CDA Level-2 考试全攻略&#xff1a;从报名到备考的保姆级教程 最近两年数据分析师认证热度持续攀升&#xff0c;CDA认证作为国内认可度较高的专业证书之一&#xff0c;Level-2考试通过率常年维持在40%左右。不同于Level-1的基础考核&#xff0c;Level-2更注重实际分析能力与统…...

前端小游戏实战:用JavaScript给爱心粒子添加点击互动效果

前端小游戏实战&#xff1a;用JavaScript给爱心粒子添加点击互动效果 当静态的爱心粒子在屏幕上跳动时&#xff0c;你是否想过让它对你的每一次点击做出回应&#xff1f;本文将带你从零开始&#xff0c;用JavaScript为爱心粒子系统添加点击生成、拖拽交互等动态效果&#xff0c…...

bilibili-api技术解析:如何解决视频标识符转换核心问题

bilibili-api技术解析&#xff1a;如何解决视频标识符转换核心问题 【免费下载链接】bilibili-api 哔哩哔哩常用API调用。支持视频、番剧、用户、频道、音频等功能。原仓库地址&#xff1a;https://github.com/MoyuScript/bilibili-api 项目地址: https://gitcode.com/gh_mir…...

OpenClaw安全防护:Phi-3-mini操作权限管控方案

OpenClaw安全防护&#xff1a;Phi-3-mini操作权限管控方案 1. 为什么需要OpenClaw安全防护 上周我在调试一个自动化文档整理任务时&#xff0c;差点酿成大错。当时OpenClaw连接的Phi-3-mini模型误解了我的指令&#xff0c;试图删除整个工作目录下的文件。虽然及时终止了进程&…...