代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作删除二叉搜索树中的节点修剪二叉搜索树
代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作&&删除二叉搜索树中的节点&&修剪二叉搜索树
主要学习内容:
二叉搜索树的插入删除操作
701.二叉搜索树中的插入操作
701. 二叉搜索树中的插入操作 - 力扣(LeetCode)
解法思路:
本质就是二叉搜索树的查找,找到插入的地方就行
val比本层结点t的值大,去右子树,小,去左子树,如果左子树或者右子树为空,直接把val赋值给它就完事。
1.函数参数和返回值
void tra(TreeNode *t,int val)
t当前节点,val是插入值
2.终止条件
本题只要赋值操作结束就是终止条件
3.本层代码逻辑
//比本层小,去左子树if(t->val>val)if(t->left)tra(t->left,val);//左子树为空,直接赋值返回else{t->left=new TreeNode(val);return;}//比本层大,去右子树elseif(t->right)tra(t->right,val);//右子树为空,直接赋值返回else{t->right=new TreeNode(val);return;}
完整代码:
class Solution {
public:void tra(TreeNode *t,int val){if(t->val>val)if(t->left)tra(t->left,val);else{t->left=new TreeNode(val);return;}elseif(t->right)tra(t->right,val);else{t->right=new TreeNode(val);return;}}TreeNode* insertIntoBST(TreeNode* root, int val) {if(root==nullptr){root=new TreeNode(val);return root;}tra(root,val);return root;}
};
注意一下检查root是否为空就行
代码随想录做法
class Solution {
public:TreeNode* insertIntoBST(TreeNode* root, int val) {if (root == NULL) {TreeNode* node = new TreeNode(val);return node;}if (root->val > val) root->left = insertIntoBST(root->left, val);if (root->val < val) root->right = insertIntoBST(root->right, val);return root;}
}
搜索过程:
if (root->val > val) root->left = insertIntoBST(root->left, val);
if (root->val < val) root->right = insertIntoBST(root->right, val);
赋值过程:
if (root == NULL) {TreeNode* node = new TreeNode(val);return node;}
比自己写的要简洁很多,特殊情况也涵盖了进去
450.删除二叉搜索树中的节点
450. 删除二叉搜索树中的节点 - 力扣(LeetCode)
思路:
先分情况讨论,没找到就不说啦,找到的话可以大致分为3种情况
1.删除的是叶子结点,直接删
2.删的结点只有一个孩子,左或者右,那就让孩子直接继承到结点所在的位置即可
3.删的结点左右都有,这个比较麻烦,我选择的策略是把当前结点的左孩子接到右孩子上面。而这样的话,我们就要找到当前结点右孩子的最左边的左孩子,让当前结点的左孩子接上去,因为这里是只比当前结点大一点点的数,比当前结点的其他数都要小,只有在这里才符合二叉搜索树定义。
例如删除7的过程,就是如上。如果没有8的话,那5也是直接去8的位置。
接下来看具体的代码实现
1.函数返回值和参数
TreeNode* deleteNode(TreeNode* root, int key)
我们直接就返回的是删除完以后的当前结点,可能是当前结点被删,也有可能是左子树或者右子树中的结点被删掉,然后一层一层往上返回。
2.本层逻辑
if(root->val>key) root->left=deleteNode(root->left,key);
if(root->val<key) root->right=deleteNode(root->right,key);
return root;
左子树不为空,遍历左子树,用当前结点的左子树承接修改完以后的左子树根结点
右子树不为空,遍历右子树,用当前结点的右子树承接修改完以后的右子树根结点
左右子树处理完以后返回当前结点
如果删除的是本层结点的话,会在终止条件中处理
3.终止条件(其实我觉得也算是本层处理逻辑了)
步骤:
1.为空那就返回null
2.如果没找到那就跳到本层逻辑去继续递归左右子树
3.找到了的话就是进入下面这个if
3.1 叶子结点 直接删
3.2 左为空或者右为空 直接让孩子继承当前结点的位置,左不为空就返回左孩子,这里是通过本层逻辑里面的root->left承接住了这个返回值,右孩子同理
3.3 左右都不为空
TreeNode *t=root->right;
while(t->left) t=t->left;
t->left=root->left;
return root->right;
1.保存一下右孩子,因为我们的策略是让左孩子接到右孩子上面。
2.找到右子树中最左边的位置,即一直循环,循环到空为止
3.将当前结点左孩子移动到右子树最左边
4.返回新的子树的根结点,即当前结点的右子树
完整的本层逻辑代码:
if(root==nullptr)return nullptr;
if(root->val==key)
{if(root->left==nullptr&&root->right==nullptr)return nullptr;else if(root->left!=nullptr&&root->right==nullptr)return root->left;else if(root->left==nullptr&&root->right!=nullptr)return root->right;else{TreeNode *t=root->right;while(t->left) t=t->left;t->left=root->left;return root->right; }
}
完整代码:
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {if(root==nullptr)return nullptr;if(root->val==key){if(root->left==nullptr&&root->right==nullptr)return nullptr;else if(root->left!=nullptr&&root->right==nullptr)return root->left;else if(root->left==nullptr&&root->right!=nullptr)return root->right;else{TreeNode *t=root->right;while(t->left) t=t->left;t->left=root->left;return root->right; } }if(root->val>key) root->left=deleteNode(root->left,key);if(root->val<key) root->right=deleteNode(root->right,key);return root;}
};
注意我们这里是逻辑上的修改,物理上还需要手动释放内存,这里不过多赘述,大家自行注意。
代码随想录有释放内存的完整版:
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {if (root == nullptr) return root; // 第一种情况:没找到删除的节点,遍历到空节点直接返回了if (root->val == key) {// 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点if (root->left == nullptr && root->right == nullptr) {///! 内存释放delete root;return nullptr;}// 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点else if (root->left == nullptr) {auto retNode = root->right;///! 内存释放delete root;return retNode;}// 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点else if (root->right == nullptr) {auto retNode = root->left;///! 内存释放delete root;return retNode;}// 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置// 并返回删除节点右孩子为新的根节点。else {TreeNode* cur = root->right; // 找右子树最左面的节点while(cur->left != nullptr) {cur = cur->left;}cur->left = root->left; // 把要删除的节点(root)左子树放在cur的左孩子的位置TreeNode* tmp = root; // 把root节点保存一下,下面来删除root = root->right; // 返回旧root的右孩子作为新rootdelete tmp; // 释放节点内存(这里不写也可以,但C++最好手动释放一下吧)return root;}}if (root->val > key) root->left = deleteNode(root->left, key);if (root->val < key) root->right = deleteNode(root->right, key);return root;}
};
669.修剪二叉搜索树
669. 修剪二叉搜索树 - 力扣(LeetCode)
思路1:上一套题的后序遍历
上一道题是判断等不等于,这道题是判断是否在一个区间没错就改一个if条件就好。
哈哈其实没有那么简单,上一套道题本质上是一个前序遍历,我们找到一个符合的值,删掉了就直接return了,没管它的子树上的值有没有在区间内,所以我们这次要换成后序遍历,这样就是从最底下开始遍历,每一个节点都不会错过。
class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if(root==nullptr)return nullptr;//左if(root->left) root->left=trimBST(root->left,low,high);//右if(root->right) root->right=trimBST(root->right,low,high);//中if(root->val>high || root->val<low){if(root->left==nullptr&&root->right==nullptr)return nullptr;else if(root->left!=nullptr&&root->right==nullptr)return root->left;else if(root->left==nullptr&&root->right!=nullptr)return root->right;else{TreeNode *t=root->right;while(t->left) t=t->left;t->left=root->left;return root->right; } }return root;}
};
思路2:后序遍历
class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if(root==nullptr)return nullptr;root->left=trimBST(root->left,low,high);root->right=trimBST(root->right,low,high);if(root->val<low)return root->right;if(root->val>high)return root->left;return root;}
};
后序遍历,本层逻辑是如果遇到小于low的直接返回右孩子,大于high的直接返回左孩子。
有人可能疑惑这样的话不就不知道右孩子和左孩子是否全都符合区间了嘛?
其实后序遍历就避免了这个问题,我们看一个例子
我们是后序遍历,所以先遍历到1发现1是,再到2,再到0发现0小于low,就返回右孩子,遍历4,4大于high,返回左孩子。
你会发现我们用后序遍历,左子树或者右子树的结点全是已经验证过的,留下的都是在区间内的。
思路3:(复习的时候再看一遍)
对根结点 root 进行深度优先遍历。对于当前访问的结点,如果结点为空结点,直接返回空结点;如果结点的值小于 low,那么说明该结点及它的左子树都不符合要求,我们返回对它的右结点进行修剪后的结果;如果结点的值大于 high,那么说明该结点及它的右子树都不符合要求,我们返回对它的左子树进行修剪后的结果;如果结点的值位于区间 [low,high],我们将结点的左结点设为对它的左子树修剪后的结果,右结点设为对它的右子树进行修剪后的结果。
class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root == nullptr) {return nullptr;}if (root->val < low) {return trimBST(root->right, low, high);} else if (root->val > high) {return trimBST(root->left, low, high);} else {root->left = trimBST(root->left, low, high);root->right = trimBST(root->right, low, high);return root;}}
};
相关文章:

代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作删除二叉搜索树中的节点修剪二叉搜索树
代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作&&删除二叉搜索树中的节点&&修剪二叉搜索树 主要学习内容: 二叉搜索树的插入删除操作 701.二叉搜索树中的插入操作 701. 二叉搜索树中的插入操作 - 力扣(LeetCode&…...

使用Apifox创建接口文档,部署第一个简单的基于Vue+Axios的前端项目
前言 在当今软件开发的过程中,接口文档的创建至关重要,它不仅能够帮助开发人员更好地理解系统架构,还能确保前后端开发的有效协同。Apifox作为一款集API文档管理、接口调试、Mock数据模拟为一体的工具,能够大幅度提高开发效率。在…...

TCP的第三次握手没有回复,会出现哪些问题现象
从三次握手的一开始来讲,刚开始客户端和服务器都处于close状态 这里不能是2次握手的原因就在于,服务器端即女孩子,无法确认客户端即男孩子,是否已经收到了,我也愿意建立连接即我也爱你,这一条最终确认的信息…...
【工具】arxiv_latex_cleaner 去除latex注释
https://github.com/google-research/arxiv-latex-cleaner/issues/24 文章目录 1.修改编码2.如何安装2.1.打包2.2.安装 3.测试功能 注意:需要创建python3.9的环境 1.修改编码 官方提供的arxiv_latex_cleaner的编码格式是有问题的,见这里。这个有位朋友说…...
macOS开发环境配置与应用开发
一、macOS开发环境配置 1. 安装Xcode Xcode 是Apple官方开发环境工具,用于macOS、iOS、watchOS和tvOS应用开发。它集成了代码编辑、编译、调试、性能分析、界面设计等功能。 下载与安装: 打开 App Store,搜索“Xcode”。 点击安装ÿ…...

15分钟学 Python :编程工具 Idea 和 vscode 中配置 Python ( 补充 )
编程工具配置 Python 在 IDE 和 VSCode 中 在编程学习的过程中,选择合适的开发工具至关重要。本文将详细介绍在两种流行的IDE(IntelliJ IDEA 和 Visual Studio Code)中如何配置Python环境,帮助你更高效地进行Python开发。 一、编…...
MyBatis 如何实现延迟加载?深度探讨 MyBatis 的延迟加载:如何优化数据访问效率
在当今的应用程序开发中,尤其是与数据库交互时,性能成为了重中之重。频繁的数据库访问会导致响应时间变慢,甚至影响用户体验。为了优化数据访问,MyBatis 提供了延迟加载(Lazy Loading)的强大功能。本文将详…...

springboot系列--web相关知识探索三
一、前言 web相关知识探索二中研究了请求是如何映射到具体接口(方法)中的,本次文章主要研究请求中所带的参数是如何映射到接口参数中的,也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、…...

AI冲击下的编程职业未来:你缺的不是技术,而是跨学科思维!
随着AIGC技术(如ChatGPT、MidJourney、Claude等大语言模型)的不断进化,AI辅助编程工具迅速普及,程序员的工作方式正在经历前所未有的转型。代码自动补全、智能化代码生成等功能大幅提升了工作效率,但与此同时ÿ…...
是否是 2 的幂次方
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。 如果存在一个整数 x 使得 n 2x ,则认为 n 是 2 的幂次方。 示例 1: 输入:n 1 输出:tr…...

音视频入门
一个视频,一秒内普遍大于等于25帧。 入门知识: 1.帧,一张画面就是一帧。一个视频就是由许许多多帧组成的。 帧率,单位时间内帧的数量。单位:帧/秒 或 fps。 分类:I帧,P帧,B帧 I…...
C++随心记 续一
C中的模板 在其它语言中如Java或者C#中可能叫做泛型,在C中为模板,泛型的限制通常比模板多。模板可以解决多次的代码重复问题,如以下场景 #include <iostream> #include <string>void print(int value) {std::cout << val…...

消息中间件:RabbitMQ
消息中间件:RabbitMQ 前言安装Window安装Linux安装 管理页面什么是RabbitMQ?入门基本概念简单队列工作队列(Work Queues)发布/订阅(Publish/Subscribe)临时队列 路由(Routing)主题&a…...

sql-labs:42~65
less42(单引号闭合、报错回显) login_useradmin login_password123 and if(11,sleep(2),1) # # 单引号闭合 login_useradmin login_password123and updatexml(1,concat(0x7e,database(),0x7e),1)# # 报错回显…...

KaTeX.js渲染数学公式
什么是KaTeX.js ? KaTeX 是一个集成速度快且功能丰富的数学公式渲染库,专为 Web 设计。它由 Khan Academy 开发,提供接近印刷品质的数学公式展示,同时保持与浏览器的高效互动性。KaTeX 特点包括快速渲染速度、高质量的输出、独立运行、跨平…...

算法训练营打卡Day19
目录 1.二叉搜索树的最近公共祖先 2.二叉树中的插入操作 3.删除二叉搜索树中的节点 题目1、二叉搜索树的最近公共祖先 力扣题目链接(opens new window) 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有…...
H.264编解码工具 - FFmpeg
一、简介 FFmpeg是一款用于处理多媒体数据的开源软件,可以完成音频、视频和多媒体流的编解码、转码、解码、录制、流媒体播放等功能。它提供了丰富的命令行工具和库函数,适用于各种平台和操作系统。 FFmpeg支持多种常见的音视频格式,包括MP3、WAV、FLAC、MP4、AVI、MKV等。它…...

60 序列到序列学习(seq2seq)_by《李沐:动手学深度学习v2》pytorch版
系列文章目录 文章目录 系列文章目录一、理论知识比喻机器翻译Seq2seq编码器-解码器细节训练衡量生成序列的好坏的BLEU(值越大越好)总结 二、代码编码器解码器损失函数训练预测预测序列的评估小结练习 一、理论知识 比喻 seq2seq就像RNN的转录工作一样,非常形象的比…...
elementPlus的tree组件点击后有白色背景
在使用elementPlus的tree组件时,需要对它进行样式的重写,下面是相关代码 <script setup> import { ref } from vue const data [{label: Level one 1,children: [{label: Level two 1-1,children: [{label: Level three 1-1-1}]}]},{label: Leve…...
【Git】Git在Unity中使用时的问题记录
个人向笔记。 (为什么没截图,因为公司电脑没法截图!) 1 前言 主要记录在使用Git协同开发时的各种问题,方便以后查阅。 2 记录 2.1 合并冲突 git pull下来后直接给合并了,麻了。若不想直接合并应该先把分…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...

Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...

如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...