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

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

文章目录

      • [450. 删除二叉搜索树中的节点](https://leetcode.cn/problems/delete-node-in-a-bst/)
        • 一、题目
        • 二、题解
          • 方法一:递归(一种麻烦的方法)
          • 方法二:优化后的递归


一、题目

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。

示例 1:

img

输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]
解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
另一个正确答案是 [5,2,6,null,4,null,7]。

示例 2:

输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]
解释: 二叉树不包含值为 0 的节点

示例 3:

输入: root = [], key = 0
输出: []

提示:

  • 节点数的范围 [0, 104].
  • -105 <= Node.val <= 105
  • 节点值唯一
  • root 是合法的二叉搜索树
  • -105 <= key <= 105

进阶: 要求算法时间复杂度为 O(h),h 为树的高度。

二、题解

方法一:递归(一种麻烦的方法)

主要思路如下:

  1. findNode 函数:这个函数用于在给定的二叉搜索树中找到值等于 target 的节点。函数采用递归的方式,在树中搜索目标节点。如果当前节点为空,说明未找到目标节点,返回 nullptr。如果当前节点的值等于目标值,返回该节点。如果当前节点的值大于目标值,说明目标节点在左子树中,递归地搜索左子树。否则,目标节点在右子树中,递归地搜索右子树。

  2. deleteNode 函数:这个函数用于删除二叉搜索树中值为 key 的节点。首先,通过调用 findNode 函数找到待删除的节点 node,同时维护一个指向 node 的父节点 pre。然后根据删除情况进行不同的处理:

    • 如果 pre 为空,说明待删除节点是根节点。然后根据左右子树的情况进行调整,保留右子树并将左子树插入右子树中的最左叶子节点。
    • 如果 pre 非空,根据 pre 的位置判断 node 是其父节点的左子节点还是右子节点。然后根据左右子树的情况进行调整,同样保留右子树并将左子树插入右子树中的最左叶子节点。

最后,删除 node 节点并返回调整后的树。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode *pre = nullptr;TreeNode* findNode(TreeNode *root, int target){if(root == nullptr){return root;}if(root->val == target){return root;}pre = root;if(root->val > target){TreeNode *left = findNode(root->left, target);return left;}else{TreeNode *right = findNode(root->right, target);return right;}}TreeNode* deleteNode(TreeNode* root, int key) {TreeNode *node = findNode(root, key);if(node == nullptr) return root;if(pre == nullptr){if(node->left && node->right){TreeNode *temp = node->right;while(temp->left){temp = temp->left;}temp->left = node->left;return node->right;}else if(node->left){return node->left;}else if(node->right){return node->right;}else{return nullptr;}}if(pre && pre -> right == node){if(node->left && node->right){TreeNode *temp = node->right;while(temp->left){temp = temp->left;}temp->left = node->left;pre->right = node->right;}else if(node->left){pre->right = node->left;}else if(node->right){pre->right = node->right;}else{pre->right = nullptr;}}if(pre && pre->left == node){if(node->left && node->right){TreeNode *temp = node->right;while(temp->left){temp = temp->left;}temp->left = node->left;pre->left = node->right;}else if(node->left){pre->left = node->left;}else if(node->right){pre->left = node->right;}else{pre->left = nullptr;}}delete node;return root;}
};
方法二:优化后的递归

算法思路

  1. 递归搜索节点: 首先,我们从根节点开始递归地搜索目标节点(值为key的节点)。

    • 如果当前节点为空,表示没有找到目标节点,直接返回空指针(nullptr)。
    • 如果当前节点的值大于目标key,说明目标节点在左子树中,递归搜索左子树。
    • 如果当前节点的值小于目标key,说明目标节点在右子树中,递归搜索右子树。
    • 如果当前节点的值等于目标key,说明找到了目标节点,继续下一步。
  2. 处理删除操作: 一旦我们找到了目标节点,有几种情况需要处理:

    • 如果目标节点没有左子树,那么我们可以用其右子树来替代这个节点,然后删除这个节点。
    • 如果目标节点没有右子树,类似地,我们可以用其左子树来替代这个节点,然后删除这个节点。
    • 如果目标节点既有左子树又有右子树,我们可以找到其右子树中最小的节点(即右子树中的最左节点,即后继节点),将该节点的值复制到目标节点上,然后递归地在右子树中删除这个后继节点。
  3. 返回根节点: 最后,无论如何都要返回当前子树的根节点。

具体实现

class Solution {
public:TreeNode* deleteNode(TreeNode* root, int key) {if (!root)return nullptr;if (root->val > key) {root->left = deleteNode(root->left, key); // 递归搜索左子树} else if (root->val < key) {root->right = deleteNode(root->right, key); // 递归搜索右子树} else {if (!root->left) { // 没有左子树,用右子树替代TreeNode* temp = root->right;delete root;return temp;} else if (!root->right) { // 没有右子树,用左子树替代TreeNode* temp = root->left;delete root;return temp;}TreeNode* temp = findMin(root->right); // 找到后继节点root->val = temp->val;root->right = deleteNode(root->right, temp->val); // 在右子树中删除后继节点}return root; // 返回根节点}private:TreeNode* findMin(TreeNode* node) {while (node->left)node = node->left;return node; // 找到最左节点,即后继节点}
};

算法分析

  • 在最坏情况下,我们需要遍历BST的高度h,即时间复杂度为O(h)。
  • 递归深度取决于树的高度,所以空间复杂度也是O(h)。

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

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

相关文章

SpringBoot校验,DTO文件中常用的注解应用案例.

在观看本篇文章之前&#xff0c;可以先参考我之前写的一篇文章 “ Spring5&#xff0c;Service层对DTO文件进行数据格式校验. ” &#xff0c;这篇文章是介绍在 Service层 对DTO文件的校验。 以下方的 CompanyDTO 文件为例&#xff0c;讲解不同的注解使用场景&#xff0c;以及…

论文阅读——Imperceptible Adversarial Attack via Invertible Neural Networks

Imperceptible Adversarial Attack via Invertible Neural Networks 作者&#xff1a;Zihan Chen, Ziyue Wang, Junjie Huang*, Wentao Zhao, Xiao Liu, Dejian Guan 解决的问题&#xff1a;虽然视觉不可感知性是对抗性示例的理想特性&#xff0c;但传统的对抗性攻击仍然会产…

每天一道leetcode:1129. 颜色交替的最短路径(图论中等广度优先遍历)

今日份题目&#xff1a; 给定一个整数 n&#xff0c;即有向图中的节点数&#xff0c;其中节点标记为 0 到 n - 1。图中的每条边为红色或者蓝色&#xff0c;并且可能存在自环或平行边。 给定两个数组 redEdges 和 blueEdges&#xff0c;其中&#xff1a; redEdges[i] [ai, bi…

Dubbo Spring Boot Starter 开发微服务应用

环境要求 系统&#xff1a;Windows、Linux、MacOS JDK 8 及以上&#xff08;推荐使用 JDK17&#xff09; Git IntelliJ IDEA&#xff08;可选&#xff09; Docker &#xff08;可选&#xff09; 项目介绍 在本任务中&#xff0c;将分为 3 个子模块进行独立开发&#xff…

LINUX学习笔记_GIT操作命令

LINUX学习笔记 GIT操作命令 基本命令 git init&#xff1a;初始化仓库git status&#xff1a;查看文件状态git add&#xff1a;添加文件到暂存区&#xff08;index&#xff09;git commit -m “注释”&#xff1a;提交文件到仓库&#xff08;repository&#xff09;git log&a…

计算机组成与设计 Patterson Hennessy 笔记(一)MIPS 指令集

计算机的语言&#xff1a;汇编指令集 也就是指令集。本书主要介绍 MIPS 指令集。 汇编指令 算数运算&#xff1a; add a,b,c # abc sub a,b,c # ab-cMIPS 汇编的注释是 # 号。 由于MIPS中寄存器大小32位&#xff0c;是基本访问单位&#xff0c;因此也被称为一个字 word。M…

Java Web常见面试题

1、JSP和Servlet有什么区别 jsp经过编译后变成类Servlet&#xff08;JSP的本质就是Servelt&#xff0c;JVM只能识别java的类&#xff0c;不能识别jsp的代码&#xff0c;于是web容器将jsp的代码编译成JVM能够识别的java类&#xff0c;也就是servelt&#xff09;jsp更擅长表现于…

【2023年11月第四版教材】《第5章-信息系统工程之系统集成(第四部分)》

《第5章-信息系统工程之系统集成&#xff08;第四部分&#xff09;》 3 系统集成3.1网络集成3.2 数据集成3.3 软件集成3.4 应用集成3.5 安全工程 3 系统集成 3.1网络集成 安全对策要点传输子系统1.常用的无线传输介质主要包括无线电波、微波、红外线等2.常用的有线传输介质主…

webpack中常见的Loader

目录 1.webpack中的loader是什么&#xff1f;配置方式 2. loader特性3.常见的loader 1.webpack中的loader是什么&#xff1f; loader 用于对模块的"源代码"进行转换&#xff0c;在 import 或"加载"模块时预处理文件 webpack做的事情&#xff0c;仅仅是分…

爬虫逆向实战(三)--天某云登录

一、数据接口分析 主页地址&#xff1a;天某云 1、抓包 通过抓包可以发现登录接口是account/login 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过“载荷”模块可以发现password、comParam_signature、comParam_seqCode是加密的 请求头是否加密&#xff1f; 无…

ElasticSearch 8.9.0 开发模式安装

ElasticSearch 8.9.0 开发模式安装 MacOS&#xff08;Apple芯片&#xff09;&#xff1a;https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.9.0-darwin-aarch64.tar.gz Linux&#xff1a;https://artifacts.elastic.co/downloads/elasticsearch/elasti…

git仓库新建上传记录

新建git仓会出现版本分支问题&#xff0c;解决过程&#xff1a; 其他的前期绑定之类的传送&#xff1a;https://blog.csdn.net/qq_37194189/article/details/130767397 大概思路&#xff1a;新建一个分支&#xff0c;上传&#xff0c;合并&#xff0c;删除分支 git branch …

4.2 C++ Boost 内存池管理库

Boost 库是一个由C/C语言的开发者创建并更新维护的开源类库&#xff0c;其提供了许多功能强大的程序库和工具&#xff0c;用于开发高质量、可移植、高效的C应用程序。Boost库可以作为标准C库的后备&#xff0c;通常被称为准标准库&#xff0c;是C标准化进程的重要开发引擎之一。…

cmake扩展(5)——file命令排除部分文件

在cmake中可以使用file命令获取需要的文件&#xff0c;并且支持正则/通配符&#xff0c;使用起来还是很方便的。 #语法file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])#example file(GLOB_RECURSE SOURCES "src/*.h" "src/*.cp…

HTTP与HTTPS的区别

面试常见问题&#xff0c;HTTPS优化总结易记版&#xff1a; 1、HSTS重定向技术&#xff1a;将http自动转换为https&#xff0c;减少301重定向 2、TLS握手优化&#xff1a;在TLS握手完成前客户端就提前向服务器发送数据 3、会话标识符&#xff1a;服务器记录下与某客户端的会…

Mac鼠标增强工具Smooze Pro

Smooze Pro是一款Mac上的鼠标手势增强工具&#xff0c;可以让用户使用鼠标手势来控制应用程序和系统功能。 它支持多种手势操作&#xff0c;包括单指、双指、三指和四指手势&#xff0c;并且可以自定义每种手势的功能。例如&#xff0c;您可以使用单指向下滑动手势来启动Expos视…

Linux 僵死进程

fork复制进程之后&#xff0c;会产生一个进程叫做子进程&#xff0c;被复制的进程就是父进程。不管父进程先结束&#xff0c;还是子进程先结束&#xff0c;对另外一个进程完全没有影响&#xff0c;父进程和子进程是两个不同的进程。 一、孤儿进程 现在有以下代码&#xff1a;…

如何计算全彩LED显示屏的像素

大屏尺寸 提供大屏的尺寸和像素点间距&#xff0c;计算大屏的分辨率是多少&#xff1f; 大屏尺寸&#xff1a;宽度>10200mm&#xff0c;高度>2025mm&#xff1b;像素点间距<1.25mm 分辨率计算 宽10200/1.258160px 高2025/1.251620px 宽&#xff1a;高 接近 5:1&a…

PHP 三元 !empty 而不是评估为真或假 可用isset()

是否可以使用速记三元来检查变量是否已设置&#xff0c;而不是是否计算结果为零或非零&#xff1f; 例如&#xff0c;我试过&#xff1a; $var 0; echo (string) $var ?: (string) false ?: 2;但由于前两个表达式的计算结果均为“0”或“false”&#xff0c;因此显示为 2。…

如何建立单元测试

快速开始 zixun-quickstart-mk3生成的项目已经配置好了基础的BaseTest,各个测试类只需要继承BaseTest就可以开始进行单元测试的编写了。 如何进行Mock 为了保证独立性和可重复执行,所有的外部依赖都需要进行Mock,SpringTest引入了Mockito作为单测Mock组件, Mickito官方文…