LeetCode题解 二叉树(十三):701 二叉搜索树的插入操作;450 删除二叉搜索树中的结点

news2024/10/5 21:14:21

701 二叉搜索树的插入操作 medium

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据保证,新值和原始二叉搜索树中的任意节点值都不同。

如果要按照题目中所说改变二叉树的原本结构来解决这道题,那这道题就会变得有些复杂。

所以,按照题意,遍历二叉树,找到空结点插入值就可以了。无论如何都可以找到一个叶子结点,满足待插入的结点要求(大于或小于当前叶子结点)。

如果使用递归法,返回类型是空(因为最终还是要返回根节点),传入参数是当前遍历到的结点值和目标值;

终止条件是找到空结点,然后给当前空结点赋值,返回即可

每次递归的处理逻辑要根据二叉搜索树的特点,如果插入值大于当前结点,就往右子树遍历;插入值小于当前结点,就往左子树遍历。

代码如下:

TreeNode* pre = nullptr;
void reversal(TreeNode* cur, int val) {
    if (cur == nullptr) {
        TreeNode* node = new TreeNode(val);
        if (pre->val > val) pre->left = node;
        else pre->right = node;
        return;
    }
    pre = cur;
    if (cur->val > val) reversal(cur->left, val);
    if (cur->val < val) reversal(cur->right, val);
    return;
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
    pre = new TreeNode(0);
    if (root == nullptr) {
        TreeNode* node = new TreeNode(val);
        root = node;
    }
    reversal(root, val);
    return root;
}

这道题使用迭代法会更明了一些,先找到空结点,然后根据前一个结点值的大小判断插入位置就好了,代码如下:

TreeNode* insertIntoBST(TreeNode* root, int val) {
    if (root == nullptr) {
        TreeNode* node = new TreeNode(val);
        return node;
    }
    TreeNode* pre = root;
    TreeNode* cur = root;
    while (cur != nullptr) {
        pre = cur;
        if (cur->val > val) cur = cur->left;
        else cur = cur->right;
    }
    TreeNode* node = new TreeNode(val);
    if (pre->val > val) pre->left = node;
    else pre->right = node;

    return root;
}

450 删除二叉搜索树中的结点 medium

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

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

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

这道题,找到要删除的结点并不难,关键是如何删除目标结点。

所以如果采用递归法找结点,关键就在于终止条件

终止条件第一步,遍历结点值一定等于目标值

终止条件第二步:

  • 如果该结点是叶子结点,最简单,直接删掉,返回空结点就行;

  • 如果该结点左孩子为空,就要把右孩子返回(同样满足二叉搜索树)

  • 如果该结点右孩子为空,就要把左孩子返回(同样满足二叉搜索树)

  • 如果该结点左右孩子都不为空,理论上肯定要用右孩子顶替当前结点,但关键问题在于如果右孩子还有左孩子(可恶的孙子),参见下图:

    未命名绘图

    如果要删除(中)结点,(中)结点的左右子树都存在,从小到大还剩下

    (小), (中,大),(大),(大,…)四个结点,代码随想录的方法是把(小)结点移动到(中,大)的左孩子,然后用(大)结点替代(中)结点。

整体代码如下:

TreeNode* deleteNode(TreeNode* root, int key) {
    if (root == nullptr) return root;
    if (root->val == key) {
        if (root->left == nullptr && root->right == nullptr) {
            delete root;
            return nullptr;
        }
        else if (root->left != nullptr && root->right == nullptr) {
            auto temp = root->left;
            delete root;
            return temp;
        }   
        else if (root->left == nullptr && root->right != nullptr) {
            auto temp = root->right;
            delete root;
            return temp;
        }
        else {
            auto node = root->right;
            // 关键点,要一直找,找到最左侧的结点
            while (node->left != nullptr) 
                node = node->left;
            node->left = root->left;
            TreeNode* temp = root;
            root = root->right;
            delete temp;
            return root;
        }
    }
    // 找到目标结点
    if (root->val > key) root->left = deleteNode(root->left, key);
    if (root->val < key) root->right = deleteNode(root->right, key);

    return root;
}

使用delete命令的目的是令代码更规范,除了随想录中的写法,只要合理都可以。

本道题同样可以使用迭代法,代码直接参考随想录中的写法:

private:
    // 将目标节点(删除节点)的左子树放到 目标节点的右子树的最左面节点的左孩子位置上
    // 并返回目标节点右孩子为新的根节点
    // 是动画里模拟的过程
    TreeNode* deleteOneNode(TreeNode* target) {
        if (target == nullptr) return target;
        if (target->right == nullptr) return target->left;
        TreeNode* cur = target->right;
        while (cur->left) {
            cur = cur->left;
        }
        cur->left = target->left;
        return target->right;
    }
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr) return root;
        TreeNode* cur = root;
        TreeNode* pre = nullptr; // 记录cur的父节点,用来删除cur
        while (cur) {
            if (cur->val == key) break;
            pre = cur;
            if (cur->val > key) cur = cur->left;
            else cur = cur->right;
        }
        if (pre == nullptr) { // 如果搜索树只有头结点
            return deleteOneNode(cur);
        }
        // pre 要知道是删左孩子还是右孩子
        if (pre->left && pre->left->val == key) {
            pre->left = deleteOneNode(cur);
        }
        if (pre->right && pre->right->val == key) {
            pre->right = deleteOneNode(cur);
        }
        return root;
    }

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

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

相关文章

渗透测试中的常用编码

渗透测试中的常用编码 页面编码 在网页设置网页编码 在中加入设置特定html标签 这样页面的编码就会变成utf-8 &#xff0c;如果没有设置编码就会使用默认 的编码&#xff0c;而浏览器默认编码与之不同就会出现乱码。 常用的有三种格式分别是 utf-8、gbk、gbk2312 ascii编码…

_Linux 进程信号-信号处理篇

文章目录前言捕捉信号1. 内核如何实现信号的捕捉2. sigaction代码验证可重入函数volatile(关键字)SIGCHLD信号实验一实验二前言 信号发送 信号处理 已经讲过&#xff0c;本章讲解信号处理最后一部分。 捕捉信号 信号捕捉过程图 经过信号捕捉过程图&#xff1a;我们知道信号…

语音文件分析

语音文件格式的重要参数 语音波形,它的这个文件,主要的格式就是采样率,那么电话或者嵌入式设备,采样率一般是8000Hz,就一秒钟8000个点,如果是PC机麦克风,那是16K,就一秒钟是16000个点,像这个CD是高保真的,音乐唱片的是用这个44.1K。 量化位数,又叫采样精度,…

绿通科技在创业板通过注册:收入依赖美国市场,张志江为实控人

2023年1月6日&#xff0c;证监会发布关于同意广东绿通新能源电动车科技股份有限公司&#xff08;下称“绿通科技”或“绿通电动车”&#xff09;、杭州国泰环保科技股份有限公司首次公开发行股票注册的批复。换句话说&#xff0c;证监会同意上述两家公司的创业板IPO注册。 同日…

【手写 Vue2.x 源码】第十一篇 - Vue 的数据渲染流程

一&#xff0c;前言 上篇&#xff0c;主要介绍了数组数据变化的观测情况&#xff0c;涉及以下几个点&#xff1a; 实现了数组数据变化被劫持后&#xff0c;已重写原型方法的具体逻辑&#xff1b;数组各种数据变化时的观测情况分析&#xff1b; 目前为止&#xff0c;数据劫持…

webpack 如何编写 loader

三种本地开发测试 loader 的方法 1. 匹配&#xff08;test&#xff09;单个 loader 你可以通过在 rule 对象使用 path.resolve 指定一个本地文件&#xff1a; webpack.config.js const path require(path);module.exports {//...module: {rules: [{test: /\.js$/,use: [{…

Ansible Automation Platform - 在 RHEL 安装 Ansible Automation Platform 2.3 环境

《OpenShift / RHEL / DevSecOps 汇总目录》 文本已在 RHEL 9 AAP 2.3 环境中进行验证。 说明&#xff1a; 本文介绍如何在一个节点上部署一套 all-in-one 的 Ansible Automation Platform 2.3 的运行环境。红帽 Ansible Automation Platform 2.3 需要至少 RHEL 8.4 以上的环…

【EHub_tx1_tx2_E100】Ubuntu18.04 + ROS_ Melodic + Velodyne VLP-16雷达 测试使用

简介&#xff1a;介绍 Velodyne VLP-16 16线激光雷达 在EHub_tx1_tx2_E100载板&#xff0c;TX1核心模块环境&#xff08;Ubuntu18.04&#xff09;下测试ROS驱动&#xff0c;打开使用RVIZ 查看点云数据&#xff0c;本文的前提条件是你的TX1里已经安装了ROS版本&#xff1a;Melod…

HashMap、HashTable、ConcurrentHashMap之间的区别及常见面试题

Java集合类有的集合类是存在线程安全的问题&#xff0c;但是由于之前对于集合类的使用都是在单线程的情况下使用的&#xff0c;不没有在多线程环境下使用&#xff0c;所以不涉及线程安全的问题&#xff1b;这篇博客着重讲解一下多线程环境下使用哈希表。HashMapHashMap本身不是…

一些开发时常用的网站或命令

目录关于gitgit下载网址git安装教程Gortoisegit下载地址关于PythonAnyWhere关于Linux压缩与解压命令关于python的相对与绝对路径使用_\_file_\_实现跨平台关于宝塔面板关于浏览器驱动下载本博客首次编辑于2023.01.04 &#xff0c;后续将持续进行更新 关于git git下载网址 gi…

Linux - 系统文件目录说明

目录/ - 根目录/bin - 用户基础二进制文件目录/boot - 静态启动文件/dev - 设备文件/etc - 配置文件/home - 主目录/lib - 基础共享库/lib64 - 64位基础共享库/lostfound - 可恢复的文件/media - 可移动媒体/mnt - 临时挂载点目录/opt - 自选软件包/proc - 内核 & 进程文件…

【Node】事件循环机制

Node 中的异步 API 定时器&#xff1a;setTimeout、setIntervalI/O 操作&#xff1a;文件读写、数据库操作、网络请求…Node 独有的 API&#xff1a;process.nextTick、setImmediate 事件循环的流程 Node 的事件循环分为 6 个阶段&#xff0c;这 6 个阶段会按顺序反复运行运行…

高并发内存池项目

文章目录一、项目介绍二、什么是内存池2.1 池化技术2.2 内存池2.3 内存池的作用2.4 malloc三、设计定长内存池四、高并发内存池整体框架设计六、threadcache6.1 threadcache整体设计6.2 threadcache哈希桶映射对齐规则6.3 编写对齐和映射的相关函数6.4 编写ThreadCache类6.5 th…

电网头条知识竞赛题库答案(自动答题)

今天教你们自动完成2023年电网头条的知识竞赛&#xff0c;小编也为大家安排好了教程&#xff0c;首先呢需要知道电网助手&#xff0c;打开电网助手网页https://wwwl.lanzouw.com/b01w803yj 为了帮到大家&#xff0c;我特地分享出来&#xff0c;希望能给大家带来一丝丝便利&…

1.3第二周 星期二Samba、FTP

目录 01 Samba文件共享服务 Samba服务基础 2.主配置文件 02 linux文件传输服务 1.用户访问的Samba 03 FTP服务概述 1.vsftp知识预备 04 操作流程&#xff1a; 1&#xff0e;使用时记得装ftp的包&#xff1a;yum install ftp -y 2&#xff0e;装完之后启动服务&am…

【北京理工大学-Python 数据分析-2.1Matplotlib库入门】

Matplotlib库的使用 Matplotlib库由各种可视化类构成&#xff0c;内部结构复杂&#xff0c;受Matlab启发。matplotlib.pyplot是绘制各类可视化图形的命令字库&#xff0c;相当于快捷方式。 import matplotlib.pyplot as plt plt.plot([3,1,4,5,2]) plt.ylabel("Grade&qu…

实战四十七:基于机器(逻辑回归随机森林线性回归)学习预测销售门店的商品销量详细教程(代码+数据)

项目概述: 使用时间序列预测来预测来Corporacin Favorita 的数据的商店销售额。 具体来说,构建一个模型来更准确地预测在不同 Favorita 商店销售的数千种商品的单位销售额。您将使用包含日期、商店和商品信息、促销和单位销售的易于理解的训练数据集来练习您的机器学习技能。…

基于java ssm springboot+VUE疫情防疫系统系统前后端分离设计和实现

基于java ssm springbootVUE疫情防疫系统系统前后端分离设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留…

Goland中使用GoPlantUml生成ER关系图

前言 Golang语言在近些年的开发语言中异军突起&#xff0c;在越来越多的公司项目中频繁出镜&#xff0c;也有越来越多的中间件选择使用Golang语言进行实现。正所谓源码之下无秘密&#xff0c;更友好地翻读源码对于理解功能特性以及后续使用非常有帮助&#xff0c;观摩学习源码也…

RMAN异地恢复-适用于数据库量比较大的场景

之前验证异地备份&#xff0c;只对数据库做个全备就备份恢复了&#xff0c;这种适用于数据库比较小的场景&#xff0c;因为如果数据库量大的话&#xff0c;备份&#xff0c;拷贝备份&#xff0c;恢复数据库的时间就比较长&#xff0c;停业务的时间就会比较长。 如果数据库比较…