在这里插入图片描述


在这里插入图片描述


🎁个人主页:工藤新一¹

🔍系列专栏:C++面向对象(类和对象篇)

​ 🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章


文章目录

  • 二叉查找树 OR 二叉排序树(BST)
    • 一、核心概念:一句话定义
    • 二、二叉查找树的删除
      • 2.1删除叶子节点
      • 2.2删除度为1的节点
      • 2.3删除分支节点
        • 2.3.1普通分支节点
        • 2.3.2根节点
    • 三、核心逻辑实现
      • 3.1二叉搜索树查找逻辑
      • 3.2二叉搜索树插入逻辑
      • 3.3二叉搜索树删除逻辑
      • 3.4丢弃递归返回值问题

二叉查找树 OR 二叉排序树(BST)

二叉查找树和二叉排序树是完全同一个概念,指的是同一种数据结构,其也是面试常客

二叉树具体的呈现:

在这里插入图片描述

删除与插入的处理方式都需以查找为基础

标准的二叉搜索树(BST)定义中:

  • 通常不允许重复元素
  • ✅ 每个节点的键值(key)都是唯一的
  • ✅ 遵循严格的大小关系:左子树 < 根节点 < 右子树

一、核心概念:一句话定义

在这里插入图片描述


二叉查找树是一种特殊的二叉树,他给它的节点立下了严格的“规矩”:对于树中的任意一节点,其左子树中所有 节点值都必须小于根节点,其右子树中所有 节点值都必须大于其根节点

这个“规矩”就是二叉查找树的灵魂,它的一切神奇特性都源于此


在这里插入图片描述


二、二叉查找树的删除

重点问题:如何填补空缺(节点),才能保持整个二叉树的结构不发生变化,且不违反其定义?

在这里插入图片描述

在这里插入图片描述


2.1删除叶子节点

删除node3:

在这里插入图片描述


2.2删除度为1的节点

删除node6:

在这里插入图片描述

我们观察node6只有左子树,那么可以直接将 node6->left(node6的前驱节点:node5)放入node6所在的位置


在这里插入图片描述

因此,删除度为1的节点,我们可以直接将其 左 or 右子树,放在(占用)当前节点所处的位置上即可(其余节点不需调整)


2.3删除分支节点


2.3.1普通分支节点

删除node7:

在这里插入图片描述

我们可以参考删除度为1节点的逻辑:将 node7的 前驱/后继节点 占用到node7的位置上


在这里插入图片描述


将 node7的前驱节点(node->left)node6放置node7 所处位置上

在这里插入图片描述


2.3.2根节点

O(logN)[查找删除根节点] * O(1)[删除] * O(logN)[查找替换根节点的根节点的后继/前驱节点] * O(1)[删除]

在这里插入图片描述


在这里插入图片描述


最终形成的逻辑结果:

在这里插入图片描述


问题:既然 node14 的后继节点是 node15,那么为什么不是:

在这里插入图片描述


这是因为,我们已知 node14 只有右子树(即node14度为1),那么我们就可以直接利用 “节点度为1” 的情况的处理方式来进行节点的替换。正常对于二叉树的树形结构不清楚时,一定是要判断对应删除的节点的前驱后继节点(处理方式:递归查找),进行对应删除替换逻辑


​ 删除度为2的节点,其实是对整个树的递归式的删除(将对应节点的前驱/后继指针删除后再替换至当前删除位置,并且可能会产生二次替换:将当前节点的前驱/后继节点的前驱/后继替换到当前前驱/后继节点的位置上;第三次替换:…)


但实际上,搜索时间复杂度最差的情况为:O(N)

从根节点–>最终的叶子节点 == 树的高度

树形结构中树的高度:logN

线性树形结构中树的高度:N(即整个树的节点个数)

在这里插入图片描述


二叉树

  • 逻辑结构上:树形结构
  • 存储结构上:线性的单链表结构,因此就有了O(N)的时间复杂度

三、核心逻辑实现

二叉搜索树(或:二叉排序树),其最主要的特点:中序遍历会得到一个递增序列,无论是 查找/排序 都可以非常高效的实现。在实际生产过程中 二叉排序树 是所运用到的最主要的树形结构


创建搜索二叉树:

// 定义树节点存储的数据类型
typedef int ElemType;// 定义树的节点类型
typedef struct BinarySearchTree
{ElemType data;struct BinarySearchTree* left, * right;// 构造函数 - 初始化字段BinarySearchTree(int val) :data(val), left(nullptr), right(nullptr) { }
} TreeNode;

3.1二叉搜索树查找逻辑

  • 1.对比待查找关键字值与当前节点的数据域

  • 2.关键字值 < 数据域:向左子树递归查找

  • 3.关键字值 > 数据域:向右子树递归查找

  • 复杂度分析:
    空间复杂度:O(1) - 无需额外定义其他空间

    时间复杂度(树的所有动作都是基于查找来执行):查找次数与树的高度(层次)等价 - O(logN)


二叉树中序遍历(递归)– 算法分离

时间复杂度:O(N)

  • 遍历函数:
void InOrderByRec(TreeNode* node)
{if (!node) return;InOrderByRec(node->left);cout << node->data << "->";InOrderByRec(node->right);
}

  • 查找函数:
TreeNode* SearchByRec(TreeNode* tree, int key)
{TreeNode* cursor = tree;if (!cursor) return nullptr;if (key == cursor->data) return cursor;if(key < cursor->data)return SearchByRec(cursor->left, key); // 只在左子树搜索if(key > cursor->data)return SearchByRec(cursor->right, key); // 只在右子树搜索
}

简洁代码(三目表达式):

C++return (key < cursor->data) ? SearchByRec(cursor->left, key) : SearchByRec(cursor->right, key); 

二叉树中序遍历(循环):

时间复杂度:O(logN)

TreeNode* SearchByFor(TreeNode* tree, int key)
{TreeNode* cursor = tree;while (cursor){if (key == cursor->data) return cursor;if (key < cursor->data) cursor = cursor->left;if (key > cursor->data) cursor = cursor->right;}return nullptr;
}

3.2二叉搜索树插入逻辑

  • 1.查找插入位置
  • 2.新建节点,执行插入动作
  • 3.返回操作标志

迭代逻辑:

bool Insert(TreeNode* tree, int key)
{if (!tree){tree = new TreeNode(key);return true;}// 1.查找插入的位置TreeNode* pos = tree, * parent = nullptr;// while循环结束意味着整个树的高度查找结束while (pos){// 迭代更新 parent 成为 pos 父节点parent = pos;if (key == pos->data) return false;if (key < pos->data) pos = pos->left;if (key > pos->data) pos = pos->right;}// 2.新建节点,执行插入动作if (key < parent->data) parent->left = new TreeNode(key);if (key > parent->data) parent->right = new TreeNode(key);return true;
}

🚫🚫🚫严重错误递归代码:

在这里插入图片描述


在这里插入图片描述


现在的疑问希望未来可以解答:

为什么需要返回递归结果?并且 A如何使最后那一节点指向 TreeNode(key)?


在这里插入图片描述


🚫🚫🚫我懂了!!!!为什么我的代码出现了巨大错误!!

在这里插入图片描述


在这里插入图片描述


3.3二叉搜索树删除逻辑

指定删除二叉搜索树中的节点:

  • 1.查找指定关键字值的节点,及其父节点

  • 2.检查待删除节点是否符合条件:

    • 缺少左子树,用右子树补位;反之;(左右子树不共存)

    • 缺少左右子树(即叶子节点)直接删除(断开与父节点的千丝万缕)

    • 左右子树都存在,按中序遍历查找当前节点的补位(替代)节点(前驱/后继)

      直接前驱:即左子树的最右节点(中序遍历节点前后指针规律)

      直接后继:即右子树的最左节点

  • 3.递归删除操作:删除替代节点

    • 删除当前节点 + 删除补位节点 + 删除补位节点的补位节点…
    • 注意1:需保留当前待删节点(递归删除:优先删除下层待删节点(遍历节点[地推] + 从下至上删除补位节点[回归])- 因此再次证明递归是一个栈结构)
    • 注意2:递归删除替代节点时,需将父节点作为树的新的树指针需保留
  • 4.使用补位节点替换原本的待删节点

    • 修改/断开链接:父节点/左/右子树

在这里插入图片描述


在这里插入图片描述


bool Delete(TreeNode* tree, int key)
{if (!tree) return false;//1.查找指定关键字值的节点,及其对应父节点TreeNode* pos = tree, *child = nullptr, *parent = nullptr;while (pos){if (key == pos->data){child = pos;// 结束最内层循环break; // 貌似不可使用 return; - 结束整个函数运行}// 若根节点为目标节点 parent == nullptr,反之迭代更新 parentparent = child; // 当前节点为父节点,向其子节点查找if (key < pos->data) pos = pos->left;if (key > pos->data) pos = pos->right;}// 开发技巧:对child进行为空判定,因为 key 可能不存在于树中,child 仍然为 nullptrif (!child) return false;// 2.检查待删除节点是否符合条件(进行递归删除):// a.左/右子树不共存 + 左/右子树同时不存在(叶子节点)if (!child->left || !child->right){
/*// 补位节点TreeNode* subNode = nullptr;if (!child->left) subNode = child->right;else subNode = child->left;subNode = child->left == nullptr ? child->right : child->left;
*/TreeNode* subNode = child->left == nullptr ? child->right : child->left;// parent 判空(删除根节点时)if (parent){// 将补位节点挂载到 parent下方// 若child 原本挂载到 parent的左子树上,就让补位节点替换到 parent左子树上(child的位置)if (parent->left == child) parent->left = subNode;if (parent->right == child) parent->right = subNode;}else // 父节点不存在 - 即删除根节点 - 直接将子树节点向上提升一个层次{// 直接将下级子树的根节点作为整个树的根节点 - 为什么可以这么做?因为左右子树不共存tree = subNode;}return true;}// b.左右子树都存在,按中序遍历查找当前节点的补位(替代)节点(前驱/后继)TreeNode* replacer = child, * replacer_parent = parent;// 查找直接前驱 - 左子树的最右节点pos = child->left;// 查找直接后继 - 右子树的最左节点:pos = child->right;while (pos){// 补位节点的父节点就是上一次循环的 replacerreplacer_parent = replacer;// 记录补位节点replacer = pos;pos = pos->right;// pos = pos->left; 直接后继(直接前驱/后继无法共存)}// 3.递归删除操作:删除替代节点// 树根 - replacer_parent; 待删除内容 - replacer->dataDelete(replacer_parent, replacer->data);// 4.使用补位节点替换原本的待删节点if (parent){// parent->left 指向待删节点if (parent->left == child)// 那么就使其指向补位节点parent->left = replacer;elseparent->right = replacer;}replacer->left = child->left;replacer->right = child->right;// 断开已删除节点与子树的联系(与父节点的联系已断开)child->left = nullptr, child->right = nullptr;
}

分段逻辑剖析:

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


3.4丢弃递归返回值问题

黄金法则:每个递归调用都应有对应的return语句来传递结果,确保返回值沿着调用链正确传递


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述
🌟 各位看官好,我是工藤新一¹呀~

🌈 愿各位心中所想,终有所致!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94933.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94933.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/94933.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stm32F4挂载emmc以及重定义printf

1.Cubemx SDIO USART 使用串口输出调试信息 FATFS Clock Configuration 防止堆栈溢出 2.Keil5 新建自定义文件夹及文件 将文件夹添加进工程 新建.c与.h文件&#xff0c;保存到自定义的文件夹&#xff0c;并添加到工程中 bsp_emmc.c #include "bsp_emmc.h" #include…

基于AI的大模型在S2B2C商城小程序中的应用与定价策略自我评估

摘要&#xff1a;本文聚焦电商行业&#xff0c;结合开源AI大模型与AI智能名片S2B2C商城小程序的技术特性&#xff0c;提出基于行业数据挖掘与自我评估的定价策略。通过分析行业价格分布与销量占比&#xff0c;结合商品设计、品牌创意度、商品丰富度及内功等评估指标&#xff0c…

中国移动云电脑一体机-创维LB2004_瑞芯微RK3566_2G+32G_开ADB安装软件教程

中国移动云电脑一体机-创维LB2004_瑞芯微RK3566_2G32G_开ADB安装软件教程简介&#xff1a;中国移动云电脑一体机-创维LB2004&#xff0c;显示器是23.8英寸1920x1080分辨率&#xff0c;安卓盒子配置是瑞芯微RK3566-四核-1.8GHz处理器-2G32G&#xff0c;预装Android11系统。具体操…

普蓝自研AutoTrack-4X导航套件平台适配高校机器人实操应用

在当前高校机器人工程、人工智能、自动化等专业的教学与科研中&#xff0c;师生们常常面临一个核心痛点&#xff1a;缺乏一套 “开箱即用、可深研、能落地” 的自主移动导航平台 —— 要么是纯仿真环境脱离实际硬件&#xff0c;要么是硬件零散需大量时间搭建&#xff0c;要么是…

2025年工会证考试题库及答案

一、单选题1.工会法人资格审查登记机关自收到申请登记表之日起(  )日内对有关申请文件进行审查&#xff0c;对审查合格者&#xff0c;办理登记手续&#xff0c;发放《工会法人资格证书》及其副本和《工会法人法定代表人证书》。A.二十B.十五C.六十D.三十答案:D 解析:第七条基…

【OpenGL】LearnOpenGL学习笔记17 - Cubemap、Skybox、环境映射(反射、折射)

上接&#xff1a;https://blog.csdn.net/weixin_44506615/article/details/150935025?spm1001.2014.3001.5501 完整代码&#xff1a;https://gitee.com/Duo1J/learn-open-gl | https://github.com/Duo1J/LearnOpenGL 一、立方体贴图 (Cubemap) 立方体贴图就是一个包含了6张2…

第十七章 ESP32S3 SW_PWM 实验

本章将介绍使用 ESP32-S3 LED 控制器(LEDC)。 LEDC 主要用于控制 LED&#xff0c;也可产生PWM信号用于其他设备的控制。该控制器有 8 路通道&#xff0c;可以产生独立的波形&#xff0c;驱动 RGB LED 等设备。 LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比&#xff…

Flink CDC如何保障数据的一致性

Flink CDC如何保障数据的一致性 前言 在大规模流处理中&#xff0c;故障是无可避免的。机器会宕机&#xff0c;网络会抖动。一个可靠的流处理引擎不仅要能高效地处理数据&#xff0c;更要在遇到这些故障时&#xff0c;保证计算结果的正确性。Apache Flink 正是因其强大的容错机…

Spring Boot 定时任务入门

1. 概述 在产品的色彩斑斓的黑的需求中&#xff0c;有存在一类需求&#xff0c;是需要去定时执行的&#xff0c;此时就需要使用到定时任务。例如说&#xff0c;每分钟扫描超时支付的订单&#xff0c;每小时清理一次日志文件&#xff0c;每天统计前一天的数据并生成报表&#x…

学习:uniapp全栈微信小程序vue3后台(6)

26.实现描述评分标签的双向数据绑定 /pages/wallpaper/picadd Array.prototype.splice() splice() 方法就地移除或者替换已存在的元素和/或添加新的元素。 二次确认 展现 确认标签 删除标签 温故知新&#xff1a; 标签&#xff1a; 关闭标签 27.uni-data-select调用云端分类…

Azure Marketplace 和 Microsoft AppSource的区别

微软的商业应用生态中&#xff0c;Azure Marketplace 和 Microsoft AppSource 是微软并行的两个主要“应用市场”&#xff08;Marketplace&#xff09;&#xff0c;它们共同构成了微软的“商业市场”&#xff08;Commercial Marketplace&#xff09;计划&#xff0c;但服务的目…

完整实验命令解析:从集群搭建到负载均衡配置(2)

一、环境准备与基础网络配置1.1 节点角色与网络规划节点角色主机名所属网段IP 地址网关核心功能Web 服务器web110.1.8.0/2410.1.8.1110.1.8.10&#xff08;后期调整为 10.1.8.20&#xff09;部署 Nginx/HTTPD&#xff0c;提供 Web 服务Web 服务器web210.1.8.0/2410.1.8.1210.1.…

uniapp H5禁止微信浏览器长按出菜单,只针对图片

一、问题描述 如图&#xff1a;uni-image>img,img {pointer-events: none;-webkit-pointer-events: none;-ms-pointer-events: none;-moz-pointer-events: none; }uni-image::before {content: ;position: absolute;top: 0;bottom: 0;left: 0;right: 0;background: transpa…

【机器学习】 15 Gaussian processes

本章目录 15 Gaussian processes 515 15.1 Introduction 515 15.2 GPs for regression 516 15.2.1 Predictions using noise-free observations 517 15.2.2 Predictions using noisy observations 518 15.2.3 Effect of the kernel parameters 519 15.2.4 Estimating the kern…

Vue加载速度优化,verder.js和element.js加载速度慢解决方法

1. 使用CDN 这里把常用的vue、vuex、elementui、echarts、axios都引入成cdn的方式 1、在index.html引入CDN 找到public/index.html在上方引入下边的cdn。 [!NOTE] 引入script的时候&#xff0c;一定要把vue.js放到最上边&#xff0c;最先引入&#xff0c;不然后边的js加载会…

49.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--扩展功能--集成网关--Refit跨服务调用

Refit是一个用于.NET平台的REST库,它可以将REST API转换为实时类型安全的接口。通过Refit,我们可以轻松实现微服务之间的跨服务调用,使服务间通信变得更加简单和类型安全。本文将介绍如何在我们的项目中使用Refit来实现微服务间的通信。 一、什么是Refit Refit是一个强大的REST…

日志ELK、ELFK、EFK

一.ELK架构Elasticsearch Logstash Kibana 数据库日志处理日志显示1.logstash的使用&#xff08;1&#xff09;input&#xff1a;输入&#xff08;2&#xff09;filter&#xff1a;处理&#xff08;3&#xff09;output&#xff1a;输出2.ELFK架构Filebeat-->Elasticsearc…

【CUDA进阶】MMA分析Bank Conflict与Swizzle(下)

目录前言1. bank conflict 分析2. 通过 padding 解决 bank conflict3. mma 搭配 wmma 实现矩阵乘法计算3.1 代码实现3.2 补充&#xff1a;stmatrix_sync 函数分析3.3 补充&#xff1a;__shfl_sync 函数详解4. swizzle 原理讲解5. swizzle 实现思路讲解结语下载链接参考前言 学习…

天气查询系统

项目要求 项目知识点 问题与解决 代码分部 结果展示 项目要求 1.显示天气预报系统界面 2.系统可以通过选择城市配置获取不同城市天气信息 3.查看实时的天气信息 &#xff08;当前温度、最高温度、最低温度、当前湿度、最高湿度、最低湿度、风向、风力、风级等信息&#x…

三重积分的对称性

文章目录前言柱面球面前言 规律作息 柱面 太牛了。我完全看不懂。实际上就类似于极坐标系。 球面 看到这么多东西&#xff0c;我真害怕。今天是 8.30 &#xff0c;不管 9.10 有没有复习完概率的强化&#xff0c;我都一定要开始套卷&#xff0c;还有专业课的复习。ϕ\phiϕ…