代码随想录 | 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.删的结点左右都有,这个比较麻烦,我选择的策略是把当前结点的左孩子接到右孩子上面。而这样的话,我们就要找到当前结点右孩子的最左边的左孩子,让当前结点的左孩子接上去,因为这里是只比当前结点大一点点的数,比当前结点的其他数都要小,只有在这里才符合二叉搜索树定义。

450.删除二叉搜索树中的节点

例如删除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的直接返回左孩子。

有人可能疑惑这样的话不就不知道右孩子和左孩子是否全都符合区间了嘛?

其实后序遍历就避免了这个问题,我们看一个例子

image-20241004214149676

我们是后序遍历,所以先遍历到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;}}
};

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

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

相关文章

CSS3练习--电商web

免责声明&#xff1a;本文仅做分享&#xff01; 目录 小练--小兔鲜儿 目录构建 SEO 三大标签 Favicon 图标 布局网页 版心 快捷导航&#xff08;shortcut&#xff09; 头部&#xff08;header&#xff09; logo 导航 搜索 购物车 底部&#xff08;footer&#xff0…

C(九)while循环 --- 军训匕首操情景

匕首操&#xff0c;oi~oi~oi~~~~~ 接下来的几篇推文&#xff0c;杰哥记录的是三大循环结构的运行流程及其变式。 本篇的主角是while循环。&#x1f449; 目录&#xff1a; while循环 的组成、运行流程及其变式关键字break 和 continue 在while 循环中的作用while 循环的嵌套题目…

微信小程序 图片的上传

错误示范 /*从相册中选择文件 微信小程序*/chooseImage(){wx.chooseMedia({count: 9,mediaType: [image],sourceType: [album],success(res) {wx.request({url:"发送的端口占位符",data:res.tempFiles[0].tempFilePath,method:POST,success(res){//请求成功后应该返…

搜索引擎相关的一段实习经历

0 前言 就是跟搜索相关的一段经历。主要工作就是建立倒排索引库相关的一些简单内容。 又翻到了以前的工作&#xff0c;权作纪念。 就是简单的封装cpp的库供python语言调用。 反正就是很多版本问题等等吧各种鬼问题。 我感觉这个思路可能还是待考证。 跨语言的调用我感觉还是不…

STM32 Hal库SDIO在FATFS使用下的函数调用关系

STM32 Hal库SDIO在FATFS使用下的函数调用关系 本文并不将FATFS的相关接口操作&#xff0c;而是将HAL在使用FATFS通过SDIO外设管理SD卡时&#xff0c;内部函数的调用逻辑&#xff0c;有助于当我们使用CUBEMX生成FATFS读取SD卡的代码时无法运行时Debug。本文也会说明一些可能出现…

Study--Oracle-09--部署Openfiler存储服务器

一路走来,所有遇到的人,帮助过我的、伤害过我的都是朋友,没有一个是敌人。 免费的存储服务器软件有FreeNAS 和 Openfiler。 其中Freenas的网站上只有i386及amd64的版本,也就是说Freenas不能支持64位版本的Intel CPU,而Openfiler则提供更全面的版本支持,在其网站上…

一个真实可用的登录界面!

需要工具&#xff1a; MySQL数据库、vscode上的php插件PHP Server等 项目结构&#xff1a; login | --backend | --database.sql |--login.php |--welcome.php |--index.html |--script.js |--style.css 项目开展 index.html&#xff1a; 首先需要一个静态网页&#x…

Linux线程(七)线程安全详解

当我们编写的程序是一个多线程应用程序时&#xff0c;就不得不考虑到线程安全的问题&#xff0c;确保我们编写的程序是一个线程安全&#xff08;thread-safe&#xff09;的多线程应用程序&#xff0c;什么是线程安全以及如何保证线程安全&#xff1f;带着这些问题&#xff0c;本…

zookeeper选举kafka集群的controller

zookeeper选举kafka集群的controller目录 文章目录 zookeeper选举kafka集群的controller目录前言一、实操体验controller的选举二、模拟controller选举四、删除controller节点 前言 kafka集群的controller是kafka集群中一个有特殊作用的broker&#xff0c;负责整个kafka集群的…

数据结构--线性表(顺序结构)

1.线性表的定义和基本操作 1.1线性表以及基本逻辑 1.1.1线性表 &#xff08;1&#xff09;n(>0)个数据元素的有限序列&#xff0c;记作&#xff08;a1,a2,...an&#xff09;&#xff0c;其中ai是线性表中的数据元素&#xff0c;n是表的长度。 &#xff08;2&#xff09;…

Redis数据库与GO(二):list,set

一、list&#xff08;列表&#xff09; list&#xff08;列表&#xff09;是简单的字符串列表&#xff0c;按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。List本质是个链表&#xff0c; list是一个双向链表&#xff0c;其元素是有序的&#xff0c;元…

51单片机系列-串口(UART)通信技术

&#x1f308;个人主页&#xff1a; 羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 并行通信和串行通信 并行方式 并行方式&#xff1a;数据的各位用多条数据线同时发送或者同时接收 并行通信特点&#xff1a;传送速度快&#xff0c;但因需要多根传输线&#xf…

计算机视觉学习路线:从基础到进阶

计算机视觉学习路线&#xff1a;从基础到进阶 计算机视觉&#xff08;Computer Vision&#xff09;是人工智能和机器学习领域中重要的分支&#xff0c;致力于让计算机能够理解和分析图像、视频等视觉信息。随着深度学习的发展&#xff0c;计算机视觉的应用变得越来越广泛&…

HTML增加文本复制模块(使用户快速复制内容到剪贴板)

增加复制模块主要是为了方便用户快速复制内容到剪贴板&#xff0c;通常在需要提供文本信息可以便捷复制的网页设计或应用程序中常见。以下是为文本内容添加复制按钮的一个简单实现步骤&#xff1a; HTML结构&#xff1a; 在文本旁边添加一个复制按钮&#xff0c;例如 <butto…

车载入行:HIL测试、功能安全测试、CAN一致性测试、UDS测试、ECU测试、OTA测试、TBOX测试、导航测试、车控测试

FOTA模块中OTA的知识点&#xff1a;1.测试过程中发现哪几类问题&#xff1f; 可能就是一个单键的ecu&#xff0c;比如升了一个门的ecu&#xff0c;他的升了之后就关不上&#xff0c;还有就是升级组合ecu的时候&#xff0c;c屏上不显示进度条。 2.在做ota测试的过程中&#xff…

【易社保-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

【黑马软件测试三】web功能测试、抓包

阶段三&#xff0c;内容看情况略过 Web功能测试链接测试表单测试搜索测试删除测试cookies/session测试数据库测试抓包工具的使用一个APP的完整测试流程熟悉APP业务流程功能测试APP专项测试兼容性安装、卸载和升级交叉测试(干扰测试)push消息测试用户体验测试 Web功能测试 通过…

Windows安装ollama和AnythingLLM

一、Ollama安装部署 1&#xff09;安装ollama 官网下载&#xff1a;https://ollama.com/download&#xff0c;很慢 阿里云盘下载&#xff1a;https://www.alipan.com/s/jiwVVjc7eYb 提取码: ft90 百度云盘下载&#xff1a;https://pan.baidu.com/s/1o1OcY0FkycxMpZ7Ho8_5oA?…

PostgreSQL 任意命令执行漏洞(CVE-2019-9193)

记一次授权攻击通过PostgreSql弱口令拿到服务器权限的事件。 使用靶机复现攻击过程。 过程 在信息收集过程中&#xff0c;获取到在公网服务器上开启了5432端口&#xff0c;尝试进行暴破&#xff0c;获取到数据库名为默认postgres&#xff0c;密码为1 随后连接进PostgreSql …

需求6:如何写一个后端接口?

这两天一直在对之前做的工作做梳理总结&#xff0c;不过前两天我都是在总结一些bug的问题。尽管有些bug问题我还没写文章&#xff0c;但是&#xff0c;我今天不得不先停下对bug的总结了。因为在国庆之后&#xff0c;我需要自己开发一个IT资产管理的功能&#xff0c;这个功能需要…