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)。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

系统架构设计专业技能 · 信息安全技术

系列文章目录 系统架构设计专业技能 网络技术&#xff08;三&#xff09; 系统架构设计专业技能 系统安全分析与设计&#xff08;四&#xff09;【系统架构设计师】 系统架构设计高级技能 软件架构设计&#xff08;一&#xff09;【系统架构设计师】 系统架构设计高级技能 …

C++入门知识点——解决C语言不足

&#x1f636;‍&#x1f32b;️Take your time ! &#x1f636;‍&#x1f32b;️ &#x1f4a5;个人主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王&#x1f525;&#x1f525;&#x1f525; &#x1f4a5;代码仓库&#xff1a;&#x1f525;&#x1f525;魔…

PgSQL中的DATE_PART使用

用法&#xff1a; DATE_PART(field, source) 这个DATE_PART()函数返回类型为double precision的值 century decade year month day hour minute second microseconds milliseconds dow doy epoch isodow isoyear timezone timezone_hour timezone_minute

扩散模型实战(四):从零构建扩散模型

推荐阅读列表&#xff1a; 扩散模型实战&#xff08;一&#xff09;&#xff1a;基本原理介绍 扩散模型实战&#xff08;二&#xff09;&#xff1a;扩散模型的发展 扩散模型实战&#xff08;三&#xff09;&#xff1a;扩散模型的应用 本文以MNIST数据集为例&#xff0c;从…

【论文阅读】CONAN:一种实用的、高精度、高效的APT实时检测系统(TDSC-2020)

CONAN&#xff1a;A Practical Real-Time APT Detection System With High Accuracy and Efficiency TDSC-2020 浙江大学 Xiong C, Zhu T, Dong W, et al. CONAN: A practical real-time APT detection system with high accuracy and efficiency[J]. IEEE Transactions on Dep…

Spring Boot 知识集锦之actuator监控端点详解

文章目录 0.前言1.参考文档2.基础介绍默认支持的端点 3.步骤3.1. 引入依赖3.2. 配置文件3.3. 核心源码 4.示例项目5.总结 0.前言 背景&#xff1a; 一直零散的使用着Spring Boot 的各种组件和特性&#xff0c;从未系统性的学习和总结&#xff0c;本次借着这个机会搞一波。共同学…

超越函数界限:探索JavaScript函数的无限可能

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4da; 前言 &#x1f4d8; 1. 函数的基本概念 &#x1f4df; 1.1 函数的定义和调用 &#x1f4df; 1.2 …

化繁为简,使用Hibernate Validator实现参数校验

前言 在之前的悦享校园的开发中使用了SSM框架&#xff0c;由于当时并没有使用参数参数校验工具&#xff0c;方法的入参判断使用了大量的if else语句&#xff0c;代码十分臃肿&#xff0c;因此最近在重构代码时&#xff0c;将框架改为SpringBoot后&#xff0c;引入了Hibernate V…

57从零开始学Java之一文详解String字符串的底层实现原理

作者&#xff1a;孙玉昌&#xff0c;昵称【一一哥】&#xff0c;另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在之前的两篇文章中&#xff0c;壹哥给大家介绍了String字符串及其常用的API方法、常用编码、正则表达…

第一讲:BeanFactory和ApplicationContext接口

BeanFactory和ApplicationContext接口 1. 什么是BeanFactory?2. BeanFactory能做什么&#xff1f;3.ApplicationContext对比BeanFactory的额外功能?3.1 MessageSource3.2 ResourcePatternResolver3.3 EnvironmentCapable3.4 ApplicationEventPublisher 4.总结 1. 什么是BeanF…

GuLi商城-前端基础Vue指令-单向绑定双向绑定

什么是指令? 指令 (Directives) 是带有 v- 前缀的特殊特性。 指令特性的预期值是:单个 JavaScript 表达式。 指令的职责是&#xff0c;当表达式的值改变时&#xff0c;将其产生的连带影响&#xff0c;响应式地作用于DOM 例如我们在入门案例中的 v-on&#xff0c;代表绑定事…

vscode + python

序 参考链接&#xff1a; 【教程】VScode中配置Python运行环境_哔哩哔哩_bilibili Python部分 Python Releases for Windows | Python.org vscode部分 Visual Studio Code - Code Editing. Redefined 一路next&#xff0c;全部勾上&#xff1a; 就可以了&#xff1a; 安装插…

盛元广通高校实验室开放预约与综合管理系统LIMS

系统概述&#xff1a; 高校实验室涉及到的课程、老师、学生多&#xff0c;管理起来费时费力&#xff0c;盛元广通高校实验室开放预约与综合管理系统LIMS提供简单易用的账号管理、实验室管理、课程管理、实验项目管理、实验时间设定&#xff1b;为学生提供简单易用的自主实验选…

设计模式篇---抽象工厂(包含优化)

文章目录 概念结构实例优化 概念 抽象工厂&#xff1a;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无须指定它们具体的类。 工厂方法是有一个类型的产品&#xff0c;也就是只有一个产品的抽象类或接口&#xff0c;而抽象工厂相对于工厂方法来说&#xff0c;是有…

ORB-SLAM2学习笔记7之System主类和多线程

文章目录 0 引言1 整体框架1.1 整体流程 2 System主类2.1 成员函数2.2 成员变量 3 多线程3.1 ORB-SLAM2中的多线程3.2 加锁 0 引言 ORB-SLAM2是一种基于特征的视觉SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;系统&#xff0c;它能够从单个、双目或RBG…

8月16日上课内容 部署LVS-DR群集

本章结构&#xff1a; 数据包流向分析: 数据包流向分析&#xff1a; &#xff08;1&#xff09;客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。 &#xf…

追踪工时和控制成本 如何选对工具

研发工作中的工时管理软件是一种用于追踪、记录和分析团队成员在项目中所花费的工作时间的工具。它有助于组织、监控和优化研发项目的进展&#xff0c;确保资源得到有效利用&#xff0c;项目按时完成&#xff0c;并提供数据支持用于决策制定和资源规划。 能够记录团队成员的工…

Visual Studio 2022 你必须知道的实用调试技巧

目录 1、什么是bug&#xff1f; 2.调试是什么&#xff1f;有多重要&#xff1f; 2.1我们是如何写代码的&#xff1f; 2.2又是如何排查出现的问题的呢&#xff1f; ​编辑 2.3 调试是什么&#xff1f; 2.4调试的基本步骤 2.5Debug和Release的介绍 3.Windows环境调试介绍…

[Vue]解决npm run dev报错node:internal/modules/cjs/loader:1031 throw err;

解决: 有2中方法&#xff0c;建议先尝试第一种&#xff0c;不行再第二种 第一种: 重新安装依赖环境 删除项目的node_modules文件夹&#xff0c;重新执行 # 安装依赖环境 npm install# 运行 npm run dev 我只用了第一种方法就可以了 &#xff0c;第二种方法从别的博主那看到…

QT中的按钮控件Buttons介绍

目录 Buttons 按钮控件 1、常用属性介绍 2、按钮介绍 2.1QPushButton 普通按钮 2.2QtoolButton 工具按钮 2.3Radio Button单选按钮 2.4CheckButton复选按钮 2.5Commam Link Button命令链接按钮 2.6Dialog Button Box命令链接按钮 Buttons 按钮控件 在Qt里&#xff0c;…