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

力扣题目链接

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;}
};

删除节点都要手动释放内存。

这题对我而言代码部分有点难。

代码随想录 (programmercarl.com)

思路

搜索树的节点删除要比节点增加复杂的多,有很多情况需要考虑,做好心理准备。

#递归

递归三部曲:

  • 确定递归函数参数以及返回值

说到递归函数的返回值,在二叉树:搜索树中的插入操作 (opens new window)中通过递归返回值来加入新节点, 这里也可以通过递归返回值删除节点。

代码如下:

TreeNode* deleteNode(TreeNode* root, int key)
  • 确定终止条件

遇到空返回,其实这也说明没找到删除的节点,遍历到空节点直接返回了

if (root == nullptr) return root;

  • 确定单层递归的逻辑

这里就把二叉搜索树中删除节点遇到的情况都搞清楚。

有以下五种情况:

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了
  • 找到删除的节点
    • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
    • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
    • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
    • 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

第五种情况有点难以理解,看下面动画:

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

动画中的二叉搜索树中,删除元素7, 那么删除节点(元素7)的左孩子就是5,删除节点(元素7)的右子树的最左面节点是元素8。

将删除节点(元素7)的左孩子放到删除节点(元素7)的右子树的最左面节点(元素8)的左孩子上,就是把5为根节点的子树移到了8的左孩子的位置。

要删除的节点(元素7)的右孩子(元素9)为新的根节点。.

这样就完成删除元素7的逻辑,最好动手画一个图,尝试删除一个节点试试。

代码如下:

if (root->val == key) {// 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点// 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点if (root->left == nullptr) return root->right;// 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点else if (root->right == nullptr) return root->left;// 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置// 并返回删除节点右孩子为新的根节点。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;}
}

这里相当于把新的节点返回给上一层,上一层就要用 root->left 或者 root->right接住,代码如下:

if (root->val > key) root->left = deleteNode(root->left, key);
if (root->val < key) root->right = deleteNode(root->right, key);
return root;

整体代码如下:(注释中:情况1,2,3,4,5和上面分析严格对应)

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;}
};

 自己的理解:

关键在于删除节点的孩子节点应该怎么放!

第五种情况难理解,嗯嗯,还没理解啊!

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

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

相关文章

使用AI 编写一个Mybatis SQL日志解析工具

使用AI 编写一个Mybatis SQL日志解析工具 背景 尝试使用智谱清言 AI 编写一个工具&#xff0c;将Mybatis SQL日志解析为可以执行的SQL语句。 操作 准备初始代码 首先网站上找了一个有BUG的简单的实现 https://blog.csdn.net/x541211190/article/details/107410409 用提示…

【WEEK4】 【DAY5】AJAX第二部分【中文版】

2024.3.22 Friday 接上文【WEEK4】 【DAY4】AJAX第一部分【中文版】 目录 8.4.Ajax异步加载数据8.4.1.新建User.java8.4.2.在pom.xml中添加lombok、jackson支持8.4.3.更改tomcat设置8.4.4.修改AjaxController.java8.4.5.新建test2.jsp8.4.5.1.注意&#xff1a;和WEB-INF平级&…

.NET Core教程:入门与实践实例

.NET Core教程&#xff1a;入门与实践实例 在信息技术飞速发展的今天&#xff0c;掌握一门高效的编程技术成为了每个开发者不可或缺的技能。在众多编程框架中&#xff0c;.NET Core以其跨平台、高性能和易扩展的特性&#xff0c;受到了广大开发者的青睐。本文将通过实例&#…

一文解析:固定电感器结构、作用及其与扼流圈和可变电感器的差异

固定电感器是一种电子元件又称固定线圈&#xff0c;用于在电路中产生固定的电感值。电感是指导致电流变化时产生电动势的能力&#xff0c;通常用亨利&#xff08;Henry&#xff09;作为单位。固定电感器的电感值是预先确定的&#xff0c;通常以特定的数值标识&#xff0c;例如1…

免疫荧光染色

每次要将手/样品伸进细胞培养箱&#xff0c;生物培养平台D 中时&#xff0c;都要喷洒酒精消毒。 以两个样品/两个培养基为例 从细胞培养箱&#xff08;37度&#xff0c;5%CO2浓度&#xff09;取出要染色的细胞&#xff1b;放在显微镜下观察&#xff0c;观察细胞是否趴壁以及细…

【正点原子FreeRTOS学习笔记】————(10)FreeRTOS时间管理

这里写目录标题 一、延时函数介绍&#xff08;了解&#xff09;二、延时函数解析&#xff08;熟悉&#xff09;三、延时函数演示实验&#xff08;掌握&#xff09; 一、延时函数介绍&#xff08;了解&#xff09; 相对延时&#xff1a;指每次延时都是从执行函数vTaskDelay()开始…

onnxruntime 中的 Gather 算子

上一篇文章中介绍了 Division by Invariant Integers using Multiplication 的原理&#xff0c;很多框架均才用该算法优化除法运算。onnxruntime 是已知实现中最为简洁的&#xff0c;因此本文结合 onnxruntime 的 Gather 实现进行介绍。 Gather 算子是一个索引类算子&#xff0…

Python(django)之单一接口展示功能前端开发

1、代码 建立apis_manage.html 代码如下&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>测试平台</title> </head> <body role"document"> <nav c…

S7-200 SMART 选型指南及详细技术参数

S7-200 SMART 选型指南 选型指南 硬件能力 功能 CPU外形结构与电源需求计算 直流安装 交流安装 电源需求与计算 S7-200 SMART CPU模块提供5VDC和24VDC电源&#xff1a; CPU有一个内部电源&#xff0c;用于为CPU、扩展模块、信号板提供电源和满足其他24 VDC用户电源需求。请使…

数据结构二叉树计算公式

这些是关于二叉树、完全二叉树、B-树以及树的深度和高度的一些基本概念和重要性质。让我们一一解读这些性质: 二叉树的计数公式:给定 n 个节点,可以构造的不同二叉树数量是 (\frac{(2n)!}{n! \cdot (n+1)!})。这是一个来自组合数学的结果,称为 Catalan 数。这个公式非常重要…

鸿蒙HarmonyOS应用开发之使用Node-API接口进行线程安全开发

场景介绍 napi_create_threadsafe_function是Node-API接口之一&#xff0c;用于创建一个线程安全的JavaScript函数。主要用于在多个线程之间共享和调用&#xff0c;而不会出现竞争条件或死锁。例如以下场景&#xff1a; 异步计算&#xff1a;如果需要进行耗时的计算或IO操作&a…

Scala介绍与环境搭建

Scala环境搭建与介绍 一、Scala环境搭建 1、环境准备与下载 2、验证Scala 3、IDEA新建项目&#xff0c;配置Scala&#xff0c;运行Hello world 二、Scala介绍 1、Scala 简介 2、Scala 概述 一、Scala环境搭建 1、环境准备与下载 JDK1.8 Java Downloads | Oracle 下载需求版本…

如何将python项目转变成deb安装包

先将python项目转变成可执行文件 1. 首先确保你的python项目可以正常执行 2.安装pyinstaller模块&#xff0c;pip install pyinstaller -i Simple Index 3.确定好你的项目的文件入口&#xff0c;也就是运行的文件.py 4. 开始打包成单文件&#xff0c;pyinstaller -F <第…

美团春招内幕揭秘:2024最全面Spring CORS面试题解析,跨域问题大师级指南!

随着现代Web应用越来越多地采用前后端分离的架构&#xff0c;跨域资源共享&#xff08;CORS&#xff09;成为了Web开发中不可或缺的一部分。对于构建复杂、响应迅速的Web应用&#xff0c;了解并正确实施CORS策略是确保应用安全、高效运行的关键。美团&#xff0c;作为中国领先的…

STM32学习笔记(6_7)- TIM定时器的编码器接口原理

无人问津也好&#xff0c;技不如人也罢&#xff0c;都应静下心来&#xff0c;去做该做的事。 最近在学STM32&#xff0c;所以也开贴记录一下主要内容&#xff0c;省的过目即忘。视频教程为江科大&#xff08;改名江协科技&#xff09;&#xff0c;网站jiangxiekeji.com 现在开…

【Java程序设计】【C00374】基于(JavaWeb)Springboot的社区疫情管理系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

教学软件哪个好?这个一站式智慧教学系统值得推荐!

过去培训机构老师授课的场景主要在线下&#xff0c;可以使用大屏幕 PPT 来完成培训的交付&#xff0c;而现在随着数字化基础设施的完善&#xff0c;同时为了尽可能覆盖更多的人&#xff0c;依赖线下的培训场景也逐步转移到线上来完成&#xff0c;因此也对在线教学工具产生了需…

服务器硬件基础知识详解

服务器硬件基础知识涵盖了多个关键组件及其功能&#xff0c;以下是详细的介绍&#xff1a; 一. 处理器&#xff08;CPU&#xff09;&#xff1a; 处理器&#xff08;CPU&#xff0c;Central Processing Unit&#xff09;&#xff0c;即中央处理器&#xff0c;是电子计算机的主…

【前端学习——css篇】3.隐藏元素的方法

https://github.com/febobo/web-interview 3.隐藏元素的方法 diplay:none; 元素在页面消失&#xff0c;并且不占据页面空间 opacity:0; 设置元素透明度&#xff0c;仅元素不可见&#xff0c;占用空间 visibility:hidden; 元素不可见&#xff0c;占据页面空间&#xff0c;无…

如何用OBD创建OceanBase集群

OBD创建集群的方式适用于迅速搭建集群以进行测试工作。但是在涉及线上环境的部署时&#xff0c;推荐采用OCP进行集群的创建与管理。 有关OBD 的一些详细信息&#xff0c;可以去 github 了解&#xff0c;GitHub - oceanbase/obdeploy: A deployer and package manager for Ocea…