算法练习-删除二叉搜索树中的节点(思路+流程图+代码)

难度参考

        难度:中等

        分类:二叉树

        难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记,旨在督促自己认真学习。

题目

        给定一个二叉搜索树的根节点root和一个值ky,删除二叉搜索树中的key对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
        示例1:
        输入:root=[5,3,6,2,4,null,7],key=3
        输出:[5,4,6,2,null,null,7]
        解释:给定需要删除的节点值是3,所以我们首先找到3这个节点,然后删除它。

        一个正确的答案是[5,4,6,2,null,nul,7]
        示例2:

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

思路

        为了执行删除操作,我们需要遵循以下步骤:

  1. 首先找到需要删除的节点,即其值等于 key 的节点。
  2. 如果节点未找到,即不存在值为 key 的节点,则不需要删除操作,直接返回根节点。
  3. 如果节点找到了,则有几种情况:
    • 节点是叶子节点(没有孩子节点):直接删除它,返回 nullptr
    • 节点有一个孩子节点:删除节点,并用其孩子节点代替自己。
    • 节点有两个孩子节点:找到右子树中的最小值节点(或左子树中的最大值节点),替换当前节点的值,然后在相应的子树中删除该最小值节点(或最大值节点)。

示例

        假设有以下的二叉搜索树:

         5
        / \
       3   6
      / \   \
     2   4   7

        我们想要删除值为 3 的节点。因此:

  1. 初始化和调用函数: 主程序创建了上述的二叉搜索树,并且呼叫了 deleteNode() 函数,命令它删除值为 3 的节点。

  2. 开始查找要删除的节点deleteNode() 函数开始在树中递归搜索值为 3 的节点。

    • 因为 3 小于根节点的值 5,它移动到根节点的左子节点,并且继续在该子树中查找。
  3. 找到要删除的节点: 节点 3 被找到了,由于它有两个子节点,我们必须采取特殊步骤去删除它。

    • 先找到需要替代当前节点的节点。这个节点是当前节点右子树中的最小值节点,或者是当前节点左子树中的最大值节点。在我们的例子中,这个替代节点是值为 4 的节点,因为它是值为 3 的节点右子树中的最小值。
  4. 替代和删除:

    • 我们将值为 4 的节点值放到值为 3 的节点。
               5
              / \
             4   6
            / \   \
           2   4   7
      
    • 然后,我们删除原本值为 4 的节点位置。
               5
              / \
             4   6
            /     \
           2       7
      
    • 在这个特定情况下,由于值为 4 的节点没有子节点,它可以直接被移除。
  5. 调整树结构:

    • 我们移除了值为 4 的节点,这造成值为 3 的原节点现在变成了值为 4。所以现在树的结构是这样的:
               5
              / \
             4   6
            /     \
           2       7
      
  1. 进行确认: 主程序中,我们再次通过中序遍历来确认树的结构。

    • 中序遍历是这样的:2, 4, 5, 6, 7。可以确认值为 3 的节点已经被删除,并且二叉搜索树的属性仍然得到保持。
  2. 程序结束: 树已更新,程序也随之结束。

梳理

        删除二叉搜索树(BST)中的节点需要考虑保持BST的特性,即对于任何节点,其左子树中的所有节点都比它小,右子树中的所有节点都比它大。对于删除操作,通常有三种情况需要处理:

  1. 删除没有子节点的节点:这是最简单的情况,可以直接将该节点移除,然后将其父节点对应的指针置为 null。

  2. 删除有一个子节点的节点:在这种情况下,需要删除节点,并将其子节点连接到该节点的父节点上。这样可以保证BST的特性不被破坏。

  3. 删除有两个子节点的节点:这是最复杂的情况,需要更多步骤来确保树的完整性。我们通常采用以下方法:

    • 用继承者替换要删除的节点:继承者可以是要删除节点的右子树中的最小值节点(称为后继),或左子树中的最大值节点(称为前驱)。以后继为例,它是右子树上最接近我们要删除节点的那个值,但又比它大的最小节点。
    • 删除继承者节点:由于找到的继承者是右子树中最小的节点,它不会有左子节点(如果有左子节点,那么这个左子节点将会比它更小,这与“是最小值”矛盾)。因此,我们可以轻易地将继承者节点提升为替换原节点位置的新节点,并处理好它的右子节点链接(如果有的话)。

        删除过程中替换继承者的原因是我们希望删除操作之后BST的特性依然被保留。后继是右子树中最小的节点,它满足在左子树中的任何节点的值都比它小,在右子树中的任何节点的值都比它大的条件。当我们用后继替换掉要删除的节点后,就可以保证整个树仍然保持BST的所有特性,进行中序遍历时节点的值仍然是有序的。

        这样操作能确保二叉搜索树在删除节点后仍然是有效的,即保持了二叉搜索树中序遍历结果为有序序列的特点。

代码

#include <iostream> // 包含输入输出流库
using namespace std; // 使用标准命名空间

// 定义二叉树节点结构
struct TreeNode {
    int val; // 节点值
    TreeNode *left; // 左子节点指针
    TreeNode *right; // 右子节点指针
    // 初始化构造函数
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 删除节点函数定义
TreeNode* deleteNode(TreeNode* root, int key) {
    if (!root) return root; // 如果节点为空,则直接返回
    if (key < root->val) { // 如果要删除的值小于根节点的值,则在左子树中删除
        root->left = deleteNode(root->left, key);
    } else if (key > root->val) { // 如果要删除的值大于根节点的值,则在右子树中删除
        root->right = deleteNode(root->right, key);
    } else { // 找到要删除的节点
        // 如果节点同时拥有左右子节点
        if (root->left && root->right) {
            TreeNode* temp = root->right;
            while (temp->left) temp = temp->left; // 找到右子树中最小的节点
            root->val = temp->val; // 将该节点的值赋给根节点
            root->right = deleteNode(root->right, temp->val); // 删除右子树中的最小节点
        } else { // 节点最多只有一个子节点
            TreeNode* temp = root->left ? root->left : root->right; // 获得非空的子节点(如果有的话)
            delete root; // 删除当前节点
            root = temp; // 用非空的子节点替换当前节点
        }
    }
    return root; // 返回修改后的树的根节点
}

// 中序遍历辅助函数定义
void inorderTraversal(TreeNode* root) {
    if (root) { // 如果节点非空
        inorderTraversal(root->left); // 遍历左子树
        cout << root->val << " "; // 访问当前节点,打印节点值
        inorderTraversal(root->right); // 遍历右子树
    }
}

// 主函数
int main() {
    // 创建并初始化一个 BST
    TreeNode* root = new TreeNode(5); // 根节点值为 5
    root->left = new TreeNode(3); // 根节点左子节点的值为 3
    root->right = new TreeNode(6); // 根节点右子节点的值为 6
    root->left->left = new TreeNode(2); // 左子节点的左子节点的值为 2
    root->left->right = new TreeNode(4); // 左子节点的右子节点的值为 4
    root->right->right = new TreeNode(7); // 右子节点的右子节点的值为 7

    // 打印初始 BST
    cout << "Initial BST (inorder traversal): ";
    inorderTraversal(root); // 以中序遍历方式打印
    cout << endl;

    int key = 3; // 要删除的节点值
    root = deleteNode(root, key); // 调用删除函数

    // 打印删除特定节点后的 BST
    cout << "BST after deleting node with key " << key << " (inorder traversal): ";
    inorderTraversal(root); // 以中序遍历方式打印
    cout << endl;

    return 0; // 主函数正确结束返回 0
}

打卡

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

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

相关文章

制作离线版element ui文档

链接&#xff1a;https://pan.baidu.com/s/1k5bsCK9WUlZobhFBLItw1g?pwdgeyk 提取码&#xff1a;geyk --来自百度网盘超级会员V4的分享 https://github.com/ElemeFE/element 克隆官方代码 使用nvm切换node版本&#xff0c;推荐使用14.0.0 http://doc.xutongbao.top/doc/#/zh…

Prompt Engineering实战-构建“哄哄模拟器”

目录 一 背景 二 “哄哄模拟器”的Prompt Prompt 的典型构成 三 操作步骤 3.1 创建对话 3.2 游戏测试 一 背景 前几天《AI 大模型全栈工程师》第二节课讲了“Prompt Engineering&#xff0c;提示工程”&#xff0c;里面提到一些prompt相关的技巧&#xff0c;原则&#xf…

浅谈分布式CAP定律、BASE理论

第一节 分布式架构设计理论与Zookeeper环境搭建 1. 分布式架构设计理论 学习Zookeeper之前,我们需要掌握一些分布式系统基础知识&#xff1a;了解分布式系统的概念、原理。 配置管理 域名服务 分布式同步 发布订阅 1. 分布式架构介绍 1.1 什么是分布式 《分布式系统原理和…

构建异步高并发服务器:Netty与Spring Boot的完美结合

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 ChatGPT体验地址 文章目录 前言IONetty1. 引入依赖2. 服务端4. 客户端结果 总结引导类-Bootstarp和ServerBootstrap连接-NioSocketChannel事件组-EventLoopGroup和NioEventLoopGroup 送书…

COMSOL接触(高度非线性)仿真常见报错及解决方法总结

前言 由于COMSOL采用隐式求解器&#xff0c;相较于使用显式求解器的Dyna、Abaqus等软件。要在COMSOL中实现结构接触这一高度非线性问题难度较大&#xff0c;报错时有发生。究其原因&#xff0c;是当物体之间相互接触时&#xff0c;物体受到的应力、运动路径会发生突变&#xff…

LabVIEW高精度微小电容测量

LabVIEW高精度微小电容测量 在电子工程和科研领域&#xff0c;精确测量微小电容值是一项有一定要求的任务&#xff0c;尤其在涉及到高精度和低成本时。设计了一种基于LabVIEW高精度微小电容测量系统&#xff0c;旨在提供一个既经济又高效的解决方案。 该系统的核心在于使用FD…

C语言中的内存函数你知道多少呢?

目录 ​编辑 1. memcpy的使用和模拟实现 1.1函数介绍 ​编辑 1.2函数的使用 1.3模拟实现 2. memmove的使用和模拟实现 2.1函数介绍 2.2函数的使用 2.3模拟实现 3. memset函数的使用 3.1函数介绍 3.2函数的使用 ​编辑 4. memcmp函数的使用 4.1函数介绍 4.2函数…

Python循环语句——range语句

一、引言 在Python编程中&#xff0c;range函数是一个内置函数&#xff0c;用于生成一个不可变的数字序列。它常被用于循环结构&#xff0c;如for循环&#xff0c;来遍历一系列的数字。尽管其使用非常基础&#xff0c;但range的强大之处在于其提供了灵活性&#xff0c;可以创建…

【2024.2.5练习】砍竹子(25分)

题目描述 题目分析 考虑题目是否满足贪心。每次施展魔法会使一段连续的竹子高度变为一半左右的平方根。根据样例&#xff0c;似乎每次让最高的竹子变短就能得到最优解。 假设魔法一次只能对一根竹子使用&#xff0c;永远不出现连续相同高度的竹子&#xff0c;那么显然无论使用…

接口幂等性

接口幂等性 如何实现幂等性实现方式一&#xff1a;数据库唯一主键实现方式二&#xff1a;Token机制实现方式三&#xff1a;数据库乐观锁实现方式四、加分布式锁 如何实现幂等性 其实实现幂等性的方案有不少&#xff0c;但是呢&#xff0c;这就得需要你根据不同的业务场景去选择…

Stable Diffusion 模型下载:Disney Pixar Cartoon Type A(迪士尼皮克斯动画片A类)

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十 下载地址 模型介绍 目前还没有一个好的皮克斯迪士尼风格的卡通模型&#xff0c;所以我决定自己制作一个。这是将皮克斯风格模型与我自己的Loras合并在一起&#xff0c;创建一个通用的…

oracle 启动命令以及ORA-01033问题处理、删除归档日志

1 启动数据库:startup 2 关闭数据库&#xff1a;Shutdown immediate 3 查看监听状态&#xff1a;lsnrctl status 4 启动监听&#xff1a;lsnrctl start 5 停止监听&#xff1a;lsnrctl stop 常见问题 1、在服务器重启后会出现&#xff0c;Oracle ORA-01033: ORAC…

渗透测试-信息打点与架构分析细节梳理

渗透测试-信息打点与架构分析细节梳理 为了保障信息安全&#xff0c;我在正文中会去除除靶场环境的其他任何可能的敏感信息 什么是网站架构 网站架构包括网站的方方面面&#xff0c;下面是常见的内容&#xff1a; 前端&#xff08;Front-End&#xff09;&#xff1a; 使用Reac…

DAY14之二叉树理论基础及递归遍历和迭代遍历

理论基础 满二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 如图所示&#xff1a; 这棵二叉树为满二叉树&#xff0c;也可以说深度为k&#xff0c;有2^k-1个节点的二叉…

Pymysql之Connection中常用API

Connection中常用API 1、open() &#xff1a;检测数据库是否连接。 connect.open&#xff1a;如果数据库连接返回Trhe&#xff0c;否则返回False。 2、ping(reconnectTrue) connect.ping(reconnectTrue):如果reconnectTrue表示连接断开后&#xff0c;重新进行连接。 import…

春节假期如何高效管理Shopee虾皮本土店?技巧都给你整理好了!

EasyBoss ERP 对于中国人最重要的春节即将来临&#xff0c;但对于运营Shopee、TikTok Shop等平台的卖家而言&#xff0c;他们的客户可不会过春节。为了不影响店铺的业绩&#xff0c;很多卖家在春节期间都还是照常运营店铺&#xff0c;但又不想错过和家人团圆的机会怎么办&…

《Java程序设计》实验报告(一)之Java语言基础

实验内容及步骤&#xff1a; 编写”hello world”应用程序。&#xff08;1&#xff09;代码&#xff1a; public class HelloWorld { public static void main(String[] args) { System.out.println("Hello world!"); } } &#xff08;2&#xff09;运行…

惟客数据地产经营分析解决方案-构建数字化经营体系,提高精细化管理能力

惟客数据地产经营分析解决方案以拉通数据底座&#xff0c;以管理行为、量化考核、预警机制为核心&#xff0c;强化对经营风险的识别和解决&#xff0c;以终为始&#xff0c;通过高频高价值场景的应用适配&#xff0c;支撑企业在数字化时代中不断创新、转型&#xff0c;提升企业…

使用Pillow来生成简单的红包封面

Pillow库&#xff08;Python Imaging Library的后继&#xff09;是一个强大而灵活的图像处理库&#xff0c;适用于Python。Pillow 库&#xff08;有时也称 PIL 库&#xff09; 是 Python 图像处理的基础库&#xff0c;它是一个免费开源的第三方库&#xff0c;由一群 Python 社区…

SSL协议是什么?关于SSL和TLS的常见问题解答

SSL&#xff08;安全套接字层&#xff09;及其后继者TLS&#xff08;传输层安全&#xff09;是用于在联网计算机之间建立经过身份验证和加密的链接的协议。尽管SSL协议在 1999年已经随着TLS 1.0的发布而被弃用&#xff0c;但我们仍将这些相关技术称为“SSL”或“SSL/TLS”。那么…