代码随想录 | 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下来后直接给合并了,麻了。若不想直接合并应该先把分…...

python学习记录6
(1)循环嵌套 可以将一个循环语句所属的语句块也可以是一个完整的一个循环语句,一般嵌套不应该超过3层。 嵌套可以是while-while、for-for,也可以是while-for。 基本图形输出:正方形,直角三角形 #输入一个数字n&…...

MongoDB 的基本使用
目录 数据库的创建和删除 创建数据库 查看数据库 删除数据库 集合的创建和删除 显示创建 查看 删除集合 隐式创建 文档的插入和查询 单个文档的插入 insertOne insertMany 查询 嵌入式文档 查询数组 查询数组元素 为数组元素指定多个条件 通过对数组元素使…...

数据揭秘:分类与预测技术在商业洞察中的应用与实践
分类与预测:数据挖掘中的关键任务 在数据挖掘的广阔天地中,分类与预测就像是一对互补的探险家,它们携手深入数据的丛 林,揭示隐藏的宝藏。 一、分类:数据的归类大师 分类是一种将数据点按照特定的属性或特征划分到不…...

学MybatisPlus
1.设置MySql的数据库 spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mp?useUnicodetrue&characterEncodingUTF-8&autoReconnecttrue&serverTimezoneAsia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: MySQL123 logging:l…...

如何使用工具删除 iPhone 上的图片背景
在 iPhone 上删除背景图像变得简单易行。感谢最近 iOS 更新中引入的新功能。如今,iOS 用户现在可以毫不费力地删除背景,而无需复杂的应用程序。在这篇文章中,您将学习如何使用各种方法去除 iPhone 上的背景。这可确保您可以选择最适合您偏好的…...

软件工程-数据流图
数据流图(Data Flow Diagram,DFD)是一种图形化技术,它描绘信息流和数据从输入移动到输出的过程中所经受的变换。 数据流图的设计原则 数据守恒原则,对于任何一个加工来说,其所有输出数据流中的数据必须能从该加工的输入数据流中…...

链式前向星(最通俗易懂的讲解)
链式前向新:用于存储图的 边集 数组 前言 当我们存储图的时候,往往会使用 邻接矩阵 或是 邻接表。 邻接矩阵 好写,但太浪费空间,节点一多就存不下; 邻接表 效率高,但涉及指 ,不好写容易出错…...

【C++设计模式】(四)创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式
文章目录 (四)创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式简单工厂模式工厂方法模式抽象工厂模式 (四)创建型模式:简单工厂模式,工厂方法模式,抽象工…...

浅析Golang的Context
文章目录 1. 简介2. 常见用法2.1 控制goroutine的生命周期(cancel)2.2 传递超时(Timeout)信息2.3 传递截止时间(Deadline)2.4 传递请求范围内的全局数据 (value) 3 特点3.1 上下文的…...

生日礼物C++代码
#include<bits/stdc.h> using namespace std; string s; int a,b; int main(){cout<<" 生日之地"<<\n;cout<<" 1.开始游戏"<<" 2.不想开始"<<\n;cin>>a;if(a1||a2){if(a2)cout<<…...