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

难度参考

        难度:中等

        分类:二叉树

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

题目

        给定一个二叉搜索树的根节点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() {// 创建并初始化一个 BSTTreeNode* root = new TreeNode(5); // 根节点值为 5root->left = new TreeNode(3); // 根节点左子节点的值为 3root->right = new TreeNode(6); // 根节点右子节点的值为 6root->left->left = new TreeNode(2); // 左子节点的左子节点的值为 2root->left->right = new TreeNode(4); // 左子节点的右子节点的值为 4root->right->right = new TreeNode(7); // 右子节点的右子节点的值为 7// 打印初始 BSTcout << "Initial BST (inorder traversal): ";inorderTraversal(root); // 以中序遍历方式打印cout << endl;int key = 3; // 要删除的节点值root = deleteNode(root, key); // 调用删除函数// 打印删除特定节点后的 BSTcout << "BST after deleting node with key " << key << " (inorder traversal): ";inorderTraversal(root); // 以中序遍历方式打印cout << endl;return 0; // 主函数正确结束返回 0
}

打卡

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

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

相关文章

第三百一十三回

文章目录 1. 概念介绍2. 实现方法2.1 obscureText属性2.2 decoration属性 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何实现倒计时功能"相关的内容&#xff0c;本章回中将介绍如何实现密码输入框.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍…

【JS逆向七】逆向某翻译网站的sign参数,并模拟生成 仅供学习

逆向日期&#xff1a;2024.02.07 使用工具&#xff1a;Node.js 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 可使用AES进行解密处理&#xff08;直接解密即可&#xff09;&#xff1a;AES加解密工具 1、打开某某网站(请使用文章开头的AES…

洛谷C++简单题小练习day9—[AHOI2017]寻找探监点

day9--[AHOI2017]寻找探监点--2.7 习题概述 题目描述 一个nn 的网格图&#xff08;标号由 1,1 开始&#xff09;上有 m 个探测器&#xff0c;每个探测器有个探测半径 r &#xff0c;问这 nn 个点中有多少个点能被探测到。 输入格式 第一行 3 个整数 n,m,r。 接下来 m 行&…

TCP 粘包/拆包

文章目录 概述粘包拆包发生场景解决TCP粘包和拆包问题的常见方法Netty对粘包和拆包问题的处理小结 概述 TCP的粘包和拆包问题往往出现在基于TCP协议的通讯中&#xff0c;比如RPC框架、Netty等 TCP 粘包/拆包 就是你基于 TCP 发送数据的时候&#xff0c;出现了多个字符串“粘”…

如何修改远程端服务器密钥

前言 一段时间没改密码后&#xff0c;远程就会自动提示CtrlAltEnd键修改密码。但我电脑是笔记本&#xff0c;没有end键。打开屏幕键盘按这三个键也没用。 解决方法 打开远程 1、远程端WINC 输入osk 可以发现打开了屏幕键盘 2、电脑键盘同时按住CtrlAlt&#xff08;若自身电…

PMP备考的三个阶段及学习方法分享

PMP证书是项目管理必备的关键技能证书&#xff0c;是具备进行项目管理的重要技能体现。无论升职加薪&#xff0c;还是从事项目管理工作&#xff0c;都非常重要。 个人主要从事产品开发工作&#xff0c;开始逐渐承担一些项目经理角色&#xff0c;但目前项目管理知识薄弱&#x…

Spring Data Envers 数据审计实战2 - 自定义监听程序扩展审计字段及字段值

上篇讲述了如何在Spring项目中集成Spring Data Envers做数据审计和历史版本查看功能。 之前演示的是业务表中已有的字段进行审计&#xff0c;那么如果我们想扩展审计字段呢&#xff1f; 比如目前对员工表加入了Audited审计&#xff0c;员工表有个字段为dept_id&#xff0c;为…

14 归并排序和其他排序

1.归并排序 2.计数排序 1. 归并排序 基本思想 建立在归并操作上的一种排序算法,采用分治法的一个典型应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;将两个有序表合成一个称为二路归并。 原数组无序&#xff0c;以中间分割为两个数组&#xff0c;…

用Jmeter进行接口测试

web接口测试工具&#xff1a; 手工测试的话可以用postman &#xff0c;自动化测试多是用到 Jmeter&#xff08;开源&#xff09;、soupUI&#xff08;开源&商业版&#xff09;。 下面将对前一篇Postman做接口测试中的接口用Jmeter来实现。 一、Jmeter 的使用步骤 打开Jme…

惊鸿一瞥-网络初识

&#x1f495;"Echo"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;惊鸿一瞥-网络初识 一.网络的发展过程 网络的发展过程是循序渐进的,大致可以分为四个阶段: 单机时代->局域网时代->广域网时代->互联网时代 单机时代:就是每个机器之间…

linux 06 磁盘管理

01.先管理vm中的磁盘&#xff0c;添加一个磁盘 第一步.vm软件&#xff0c;打开虚拟机设置&#xff0c;添加硬盘 第二步.选择推荐scsi 第三步.创建一个新的虚拟磁盘 第四步. 第五步. 02.在创建好的vm虚拟机中查看刚才创建的磁盘 在centos中/dev 目录是设备目录 sda是磁盘…

S7-1200PLC通讯问题总结

文章目录 一、硬件1.串口通信RS232RS485RS422 2.网口通信 二、协议1.串口通信协议2.网口通信协议 三、程序编写1.S7通信PUTGET 2.开放式以太网通信 一、硬件 可分为PLC与PLC通信&#xff0c;PLC与上位机通信&#xff0c;PLC与变频器通信&#xff0c;PLC与仪器仪表通信&#xf…