Studying-代码随想录训练营day20| 235.二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

第二十天,二叉树part07,二叉树搜索树加油加油💪

目录

235.二叉搜索树的最近公共祖先

701.二叉搜索树中的插入操作

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

拓展:普通二叉树的删除方式 

总结


235.二叉搜索树的最近公共祖先

文档讲解:代码随想录二叉搜索树的最近公共祖先

视频讲解:手撕二叉搜索树的最近公共祖先

题目:

学习:

昨天我们是在普通二叉树内找到公共祖先,采取的是后序遍历的方式,遍历所有的节点,直到找到目标值为止进行返回。

但本题是二叉搜索树,我们可以利用二叉搜索树的特点来进行公共祖先的查找。我们知道二叉搜索树中每个节点的左子树中的所有值一定小于该节点,右子树中的所有值一定大于该节点。依据此特点我们可以把遍历过程分为三种情况。

  1. p节点的值和q节点的值都小于当前遍历节点的值,则在当前节点的左子树进行寻找。
  2. p节点的值和q节点的值都大于当前遍历节点的值,则在当前节点的右子树进行寻找。
  3. p节点的值和q节点的值其中一个等于当前遍历节点的值,或者分别大于,小于当前遍历节点的值,则立马返回当前遍历的节点,该节点就是最小公共祖先。原因:①如果当前节点是p节点或者q节点的其中一个,由于我们是从上至下遍历的,因此剩下一个节点肯定在该节点的子树中,因此当前节点就是最小公共祖先。②当前节点不是p节点或者q节点,此时由于p节点和q节点分别位于当前节点的左右子树之中,因此当前节点肯定是最小公共祖先,否则往左遍历将错过右子树中的节点,往右遍历将错过左子树中的节点。

代码:递归法

//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
private:TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {if (cur == NULL) return cur;// 中if (cur->val > p->val && cur->val > q->val) {   // 左TreeNode* left = traversal(cur->left, p, q);return left;}else if (cur->val < p->val && cur->val < q->val) {   // 右TreeNode* right = traversal(cur->right, p, q);return right;}else { //第三种情况return cur;}}
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {return traversal(root, p, q);}
};

代码:迭代法

//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if (root == NULL) return NULL;TreeNode* answer = root; //返回数组while(answer) {//依据二叉搜索树的特点,分为三种情况if(p->val > answer->val && q->val > answer->val) {answer = answer->right;}else if(p->val < answer->val && q->val < answer->val) {answer = answer->left;}else { //第三种情况break;}}return answer;}
};

701.二叉搜索树中的插入操作

文档讲解:代码随想录二叉搜索树中的插入操作

视频讲解:手撕二叉搜索树中的插入操作

题目:

学习:对于二叉搜索树来说只要插入的数据和原始二叉树中的节点不同,则肯定能插入树中并成为一个叶子节点。本题其实不用考虑题目中的改变树的结构的插入方式。

代码:递归法(本题需要插入节点并将更改后的树层层返回)

//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
public:TreeNode* insertIntoBST(TreeNode* root, int val) {//确定终止条件if (root == nullptr) {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;}
};

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

文档讲解:代码随想录删除二叉搜索树中的节点

视频讲解:手撕删除二叉搜索树中的节点

题目: 

学习:

本题需要删除二叉搜索树的节点,我们首先要分析的是,删除的情况有哪些:

  1. 要删除的节点在二叉树中不存在,二叉树不需要修改。
  2. 要删除的是叶子节点,那直接删除叶子节点,返回nullptr即可。
  3. 要删除的是中间节点,但中间节点左孩子为空,右孩子不为空,让右孩子替代删除节点。
  4. 要删除的是中间节点,但中间节点右孩子为空,左孩子不为空,让左孩子替代删除节点。
  5. 要删除的节点中左右孩子都不为空,则可以将删除节点的左子树的头节点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。(补充一种我采取的方式,使用删除节点的后继或者前序来替代删除节点,但处理会复杂一些)

第5中情况动画说明:

代码:递归法

//时间复杂度O(n)
//空间复杂度O(n)
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;}
};

代码:递归法(使用后继作为代替)

//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {//确定终止条件//没有找到的话if(root == nullptr) return root;//找到了的话,分为4种情况if(root->val == key) {//第一种情况,要删除的是叶子节点,则直接返回空if(root->left == nullptr && root->right == nullptr) {//内存释放delete root;return nullptr;}//第二种情况,要删除的是中间节点,但是左子树为空,使用右子树节点替代else if(root->left == nullptr && root->right != nullptr) {TreeNode* tmp = root;root = root->right;delete tmp;return root;}//第三种情况,要删除的是中间节点,但是右子树为空,使用左子树节点替代else if(root->left != nullptr && root->right == nullptr) {TreeNode* tmp = root;root = root->left;delete tmp;return root;}//第四种情况,要删除的是中间节点,且左右子树都不为空//使用前驱或者后继节点替代else {//使用后继节点替代,即右子树的最左边的节点。TreeNode* cur = root->right; TreeNode* pre = nullptr; //指向cur的前一个节点while(cur->left != nullptr ) {pre = cur;cur = cur->left;}if(pre == nullptr) { //说明一次循环没有进行,删除节点的右子树的根节点没有左边部分,则其自身就是删除节点的后继cur->left = root->left; //把删除节点的左子树保留delete root;return cur;}else { //说明至少进入了循环一次pre->left = cur->right; //先把cur分理出,并保存cur的右子树(可能为空)cur->left = root->left; //将删除节点的左右子树释放cur->right = root->right;delete root;return cur;}}}root->left = deleteNode(root->left, key);root->right = deleteNode(root->right, key);return root;}
};

拓展:普通二叉树的删除方式 

学习:对于普通二叉树的节点删除来说,可以通过和叶子节点交换值得方式,然后再进行删除。

代码中目标节点(要删除的节点)被操作了两次:第一次是和目标节点的右子树最左面节点交换。第二次直接被NULL覆盖了。

代码:

class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {if (root == nullptr) return root;if (root->val == key) {if (root->right == nullptr) { // 这里第二次操作目标值:最终删除的作用return root->left;}TreeNode *cur = root->right;while (cur->left) {cur = cur->left;}swap(root->val, cur->val); // 这里第一次操作目标值:交换目标值其右子树最左面节点。}root->left = deleteNode(root->left, key);root->right = deleteNode(root->right, key);return root;}
};

总结

二叉搜索树的特点要牢记,利用好二叉树的特点能够更有效的进行算法设计。

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

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

相关文章

洁净室(区)浮游菌检测标准操作规程及GB/T 16292-2010测试方法解读

洁净室(区)空气中浮游菌的检测。洁净区浮游菌检测是一种评估和控制洁净区(如实验室、生产车间等)内空气质量的方法。这种检测的目的是通过测量空气中的微生物(即浮游菌)数量&#xff0c;来评估洁净区的清洁度或污染程度。下面中邦兴业小编带大家详细看下如何进行浮游菌采样检测…

RK3568技术笔记九 编译Linux详细介绍

在编译前需要按照前面的方法始化编译环境&#xff0c;否则会导致编译失败&#xff08;若配置过则无需重复配置&#xff09;。 全自动编译包含所有镜像编译&#xff0c;包括&#xff1a;uboot编译、Kernel编译、Recovey编译、文件系统编译、编译完成镜像的更新与打包。 按照前面…

阅读笔记——《Large Language Model guided Protocol Fuzzing》

【参考文献】Meng R, Mirchev M, Bhme M, et al. Large language model guided protocol fuzzing[C]//Proceedings of the 31st Annual Network and Distributed System Security Symposium (NDSS). 2024.&#xff08;CCF A类会议&#xff09;【注】本文仅为作者个人学习笔记&a…

2024ciscn 华东北awdp pwn部分wp

pwn1 break 很简单&#xff0c;栈可执行。 先格式化字符串泄露出栈地址和canary&#xff0c;然后稍稍布置一下打orw就行 沙盒和没有一样 from pwn import *context(archamd64, oslinux)if __name__ __main__:# io remote(192.47.1.39, 80)io remote(192.168.142.137, 123…

初阶 《操作符详解》 10. 逗号表达式

10. 逗号表达式 exp1, exp2, exp3, …expN 注&#xff1a; 1.逗号表达式&#xff0c;就是用逗号隔开的多个表达式 2.逗号表达式&#xff0c;从左向右依次执行&#xff0c;整个表达式的结果是最后一个表达式的结果 代码1 #include <stdio.h> int main() {int a 1;int b…

解决ubuntu18.04 安装vscode 报依赖库错误,以及打不开终端的问题。

其实很简单&#xff0c;ubuntu18.04太老了&#xff0c;官网最新版本的vscode对ubuntu18.04会有些依赖库的问题。 一顿查资料后发现2023.11月的1.85版本正常使用&#xff0c;于是完美解决。 下载链接 Visual Studio Code November 2023 点击这里下载。 下载完成&#xff0c;…

数据结构需要每个都具体实现吗?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「数据结构的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;用c的stl能刷算法题是不…

clean code-代码整洁之道 阅读笔记(第十三章)

第十三章 并发编程 "对象是过程的抽象。线程是调度的抽象。" --James O Coplien 13.1 为什么要并发 并发是一种解耦策略。它帮助我们把做什么&#xff08;目的&#xff09;和何时&#xff08;时机&#xff09;做分解开。在单线 程应用中&#xff0c;目的与时机紧密耦…

【OpenCV 图像处理 Python版】OpenCV 简介及安装

文章目录 1.OpenCV 介绍1.1 OpenCV 的特点1.2 OpenCV 的主要模块1.3 OpenCV 的应用场景 2.OpenCV-Python 库3.OpenCV 安装 1.OpenCV 介绍 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它由英特尔公司于1999年…

API的优势及应用场景(淘宝API测试的详细步骤)

一、API的优势 API的出现为应用程序间的通信提供了一种新的方式&#xff0c;它有以下优势&#xff1a; 1、降低开发难度 开发者可以通过API访问其他应用程序的数据和功能&#xff0c;避免了重复开发&#xff0c;降低了开发难度。 2、提高开发效率 API提供了一种标准化的通…

使用AES,前端加密,后端解密,spring工具类,直接c就完事了

学习python的时候&#xff0c;看到很多会对参数进行加密&#xff0c;于是好奇心驱使下&#xff0c;让我去了解了下AES加密如何在java中实现。 首先 npm install crypto-js 然后在你的方法中&#xff0c;给你们前端源码看看&#xff0c;因为我用的ruoyi框架做的实验&#xff…

构建一个检索增强生成(RAG)应用程序

:::tips 此文档是LangChain官方教程的实践总结&#xff1a;https://python.langchain.com/v0.2/docs/tutorials/rag/实践前你需要准备&#xff1a;OPENAI_API_KEY Generator&#xff1a;根据检索到的信息和用户的查询生成自然语言的回答。LANGCHAIN_API_KEY 密切监控和评估您的…

【自然语言处理系列】掌握NLP基础:去停用词、词性标注与命名实体识别实战教程

摘要&#xff1a;本系列教程专注于自然语言处理&#xff08;NLP&#xff09;中的基础元素&#xff0c;包括去停用词、词性标注以及命名实体识别。这些步骤是文本预处理和分析不可或缺的组成部分。我们将通过具体的实例和技术演示&#xff0c;讲解如何使用Python及其相关库&…

网络安全之Windows提权(上篇)(高级进阶)

目录 一&#xff0c;什么是提权&#xff1f; 二&#xff0c;提权的前提 三&#xff0c;如何提权&#xff1f; 1&#xff0c;第一步连接服务器 2&#xff0c;提升权限至iuser​编辑 3&#xff0c;利用补丁漏洞提权至最高级 四&#xff0c;总结 一&#xff0c;什么是提权&am…

大数据集群数据传输

简单的服务器间的通信示例 netcat&#xff0c;简写为 nc&#xff0c;是 unix 系统下一个强大的命令行网络通信工具&#xff0c;用于在两台主机之间建立 TCP 或者 UDP 连接&#xff0c;并提供丰富的命令进行数据通信。nc 在网络参考模型属于应用层。使用 nc 可以做很多事情&…

重磅丨上海容大推出“容聆”智能拾音工牌,赋能线下门店运营数字化

近日&#xff0c;继豚音营业厅智能质检终端之后&#xff0c;上海容大数字技术有限公司&#xff08;简称“上海容大”&#xff09;在线下面对面沟通场景下语音数据采集与智能分析领域取得了新突破&#xff0c;重磅推出AI智能语音工牌产品——“容聆”。 据悉&#xff0c;“容聆”…

mybatis x插件的使用教程(详细)

MyBatisX 的主要功能 代码生成&#xff1a; 自动生成 MyBatis 的 Mapper、XML 配置文件和实体类&#xff0c;大大减少手工编写代码的工作量。 智能代码补全&#xff1a; 提供 SQL 语句和 MyBatis 配置的智能代码补全功能&#xff0c;使开发者能够更快地编写代码。 代码导航&…

铀的危害和应用,以及铀的分离提纯

铀是一种锕系放射性元素&#xff0c;对人体存在一定的危害&#xff0c;如辐射损伤、呼吸系统损伤、神经系统损伤、免疫系统损伤等。 1、辐射损伤&#xff1a;铀的放射性会对人体产生辐射损伤&#xff0c;长期接触会增加患癌症的风险。此外&#xff0c;还可能对人体正常细胞产生…

【网络安全的神秘世界】解决dvwa靶场报错:Illegal mix of collations for operation ‘UNION‘

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 &#x1f6a9;问题描述 当尝试执行如下 SQL 语句时&#xff1a; 1 union select schema_name,1 from information_schema.s…

如何挑选护眼灯?一分钟带你了解挑选护眼灯的六大准则!

小时候&#xff0c;对正确用眼知识一无所知&#xff0c;也不明白何种光线环境对眼睛最为友善&#xff0c;结果如今的近视度数已濒临千度大关。虽然早已习惯佩戴眼镜的生活&#xff0c;但近视所带来的诸多不便仍旧在日常生活中无处不在。因此&#xff0c;对于家中孩子的视力健康…