代码随想录算法训练营第20天 | 题目: 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

news2024/10/6 20:38:38

代码随想录算法训练营第20天 | 题目: 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

文章来源:代码随想录

题目名称: 235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

235. 二叉搜索树的最近公共祖先

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:

所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉搜索树中。

第一想法:

使用二叉搜索树的性质,当寻找公共祖先,则公共祖先在[p,q]的区间之中,从根节点遍历判断,大于这个区间则向左,小于则向右,直到存在于这个区间中。

解答思路:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
        if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
        return root;
    }
}

收获:

当是二叉搜索树,其性质找公共祖先节点更容易。

题目名称:701.二叉搜索树中的插入操作

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

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。
在这里插入图片描述

提示:

给定的树上的节点数介于 0 和 10^4 之间
每个节点都有一个唯一整数值,取值范围从 0 到 10^8
-10^8 <= val <= 10^8
新值和原始二叉搜索树中的任意节点值都不同

第一想法:

通过大小判定val插入位置,如果是遍历得到空节点返回插入的节点,返回后根据大小添加在左右子树位置。

解答思路:

1.确定递归函数参数以及返回值
参数就是根节点指针,以及要插入元素,这里递归函数要不要有返回值呢?

可以有,也可以没有,但递归函数如果没有返回值的话,实现是比较麻烦的,下面也会给出其具体实现代码。

有返回值的话,可以利用返回值完成新加入的节点与其父节点的赋值操作。(下面会进一步解释)

递归函数的返回类型为节点类型TreeNode * 。

代码如下:

TreeNode* insertIntoBST(TreeNode* root, int val)

2.确定终止条件
终止条件就是找到遍历的节点为null的时候,就是要插入节点的位置了,并把插入的节点返回。

代码如下:

if (root == NULL) {
    TreeNode* node = new TreeNode(val);
    return node;
}

这里把添加的节点返回给上一层,就完成了父子节点的赋值操作了,详细再往下看。

确定单层递归的逻辑
此时要明确,需要遍历整棵树么?

别忘了这是搜索树,遍历整棵搜索树简直是对搜索树的侮辱。

搜索树是有方向了,可以根据插入元素的数值,决定递归方向。

代码如下:

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

到这里,大家应该能感受到,如何通过递归函数返回值完成了新加入节点的父子关系赋值操作了,下一层将加入节点返回,本层用root->left或者root->right将其接住。

收获:

遍历边的写法和遍历结点的解法还需要重新区分。

题目名称:450.删除二叉搜索树中的节点

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

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

首先找到需要删除的节点; 如果找到了,删除它。 说明: 要求算法时间复杂度为 O ( h ) O(h) O(h),h 为树的高度。

示例:
在这里插入图片描述

第一想法:

删除节点的操作更复杂,利用二叉搜索树的性质找到对应的节点,然后列举可能遇到的删除操作。

解答思路:

递归
递归三部曲:

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

代码如下:

TreeNode* deleteNode(TreeNode* root, int key)

2.确定终止条件
遇到空返回,其实这也说明没找到删除的节点,遍历到空节点直接返回了

if (root == nullptr) return root;

3.确定单层递归的逻辑
这里就把二叉搜索树中删除节点遇到的情况都搞清楚。

有以下五种情况:

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

代码如下:

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的右孩子作为新root
        delete 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;
class Solution {
  public TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return root;
    if (root.val == key) {
      if (root.left == null) {
        return root.right;
      } else if (root.right == null) {
        return root.left;
      } else {
        TreeNode cur = root.right;
        while (cur.left != null) {
          cur = cur.left;
        }
        cur.left = root.left;
        root = root.right;
        return root;
      }
    }
    if (root.val > key) root.left = deleteNode(root.left, key);
    if (root.val < key) root.right = deleteNode(root.right, key);
    return root;
  }
}

困难:

这里如果左右子树都存在的节点去除时需要考虑更多,如何具体操作是个问题,需要使用一个while去找到右子树的最左叶子节点。

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

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

相关文章

【.Net】Web项目部署腾讯云

文章目录 总述前置准备docker-compose部署普通部署 参考 总述 前置准备 云服务添加端口 另有linux本身防火墙请参考&#xff1a; 【Linux】防火墙命令 需安装.Net SDK和Asp .Net Runtime 注意&#xff1a; 1、sdk也要不只是runtime 2、是Asp .Net Runtime不是.Net Runtime …

Linux socketcan应用编程

一、基本步骤 1、打开并绑定到 CAN 套接字 在执行任何操作之前&#xff0c;第一步是创建一个套接字。此函数接受三个参数 – 域/协议系列 &#xff08;PF_CAN&#xff09;、套接字类型&#xff08;原始或数据报&#xff09;和套接字协议。如果成功&#xff0c;该函数将返回文件…

游戏AI的创造思路-技术基础-tanh函数详解

又来搞事情&#xff0c;总想着把sigmoid函数替换成其他函数作为激活函数&#xff0c;或者找到更合适某一段训练的函数&#xff0c;所以今天来聊聊tanh函数&#xff08;谁让咱当年差点去了数学系&#xff0c;结果还是在数学系转过去计算机的&#xff09; 目录 3.9. tanh函数详解…

Springboot整合Redis以及业务工具类示例

docker安装Redis参考我另一篇博客Docker安装Redis及持久化 一、Get-Started 依赖 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --> <dependency><groupId>org.springframework.boot</groupId>…

轻松创建对象——简单工厂模式(Python实现)

1. 引言 大家好&#xff0c;又见面了&#xff01;今天我们要聊的是设计模式中的“万能钥匙”——简单工厂模式。想象一下&#xff0c;如果每次你都得亲自动手创建各种对象&#xff0c;不仅累得像个陀螺&#xff0c;还可能搞得一团糟。别怕&#xff0c;简单工厂模式来拯救你&am…

nextTick实现原理及使用场景

1.定义&#xff1a; nextTick是一个在Vue.js中常见的异步更新DOM的机制&#xff0c;它利用JavaScript的事件循环机制以及浏览器的渲染流程来实现延迟执行DOM更新操作。nextTick方法能够将回调函数延迟到下一个DOM更新循环之后执行&#xff0c;确保在DOM更新完成后执行某些操作…

【Linux系统】CUDA的安装

今天在安装环境时遇到报错&#xff1a; The detected CUDA version (10.1) mismatches the version that was used to compile PyTorch (11.8). Please make sure to use the same CUDA versions. 报错原因&#xff1a;安装的cuda版本不对应&#xff0c;我需要安装cuda的版本…

宠物空气净化器哪个品牌性价比高?宠物空气净器Top3品牌推荐

养猫确实给家庭带来了无尽的欢乐&#xff0c;但猫毛无处不在的问题确实让不少猫主人感到头疼。不论是长毛猫还是短毛猫&#xff0c;它们掉落的浮毛飘浮在空气中&#xff0c;不仅影响家居环境的整洁度&#xff0c;还可能成为过敏的源头。因此&#xff0c;如何高效地处理这些猫浮…

多模态融合 + 慢病精准预测

多模态融合 慢病精准预测 慢病预测算法拆解子解法1&#xff1a;多模态数据集成子解法2&#xff1a;实时数据处理与更新子解法3&#xff1a;采用大型语言多模态模型&#xff08;LLMMs&#xff09;进行深度学习分析 慢病预测更多模态 论文&#xff1a;https://arxiv.org/pdf/2406…

创新校园服务模式 跑腿小程序平台源码构建与实践 前后端分离 带完整的安装代码包以及部署教程

系统概述 本项目是一个集任务发布、接单、支付、评价于一体的跑腿服务小程序平台&#xff0c;专为高校校园设计。系统采用前后端分离架构&#xff0c;前端负责用户界面展示和交互逻辑&#xff0c;后端处理业务逻辑、数据存取等&#xff0c;两者通过API接口进行通信&#xff0c…

『手撕Vue-CLI』 添加自定义指令

添加 create 指令 在 vue-cli 中&#xff0c;create 指令是用来创建一个新的项目的&#xff0c;我实现的 nue --help 的帮助信息中只有 --version&#xff0c;--help 这两个指令&#xff0c;所以当用户使用我的 nue-cli 时&#xff0c;并不知道有 create 这个指令&#xff0c;所…

Conan安装与C++第三方环境配置保姆级图文教程(附速查字典)

目录 1 什么是Conan&#xff1f;2 Conan安装与配置3 Conan的常见操作3.1 搜索指定包3.2 安装指定包3.3 本地包管理3.4 查看项目依赖 4 Conan构建项目案例 1 什么是Conan&#xff1f; Conan是一个开源的C/C包管理器&#xff0c;用于管理和构建C/C项目所需的依赖库。传统上&…

BIOS设置与系统分区

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 目录 一BIOS 1破解密码的前提 2B…

CrossViT:用于图像分类的交叉注意多尺度Vision Transformer

提出了一种双支路Transformer来组合不同大小的图像补丁(即变压器中的令牌)以产生更强的图像特征。方法处理具有不同计算复杂度的两个独立分支的小补丁和大补丁令牌,然后这些令牌纯粹通过注意多次融合以相互补充。此外,为了减少计算量,开发了一个简单而有效的基于交叉关注的令…

98 - IDEA远程调试服务器Java程序

Java 提供了一套标准的调试协议&#xff08;JDWP - Java Debug Wire Protocol&#xff09;&#xff0c;允许调试器&#xff08;IDE&#xff09;与被调试程序&#xff08;应用&#xff09;之间进行通信。 1.服务器特定命令启动程序 在服务器上以以下命令启动Java程序 java -a…

linux 离线安装docker

测试服务器&#xff1a;银河麒麟V10 x86_64 注意&#xff1a;推荐使用国内的镜像站下载&#xff0c;因为官网不挂梯子无法访问&#xff0c;我用的是清华大学开源软件镜像站 一、下载离线包&#xff1a; 官网下载docker离线包 下载地址&#xff1a;https://download.docker.c…

老师怎样一键发布期末考试成绩?

期末考试的钟声一响&#xff0c;老师们便开始了紧张的阅卷工作。成绩出来后&#xff0c;他们又面临着一项繁琐的任务——将成绩单逐一私信给每位学生的家长。这不仅耗费了大量时间&#xff0c;也让老师们在繁忙的期末工作中倍感压力。期末老师的工作已经够多够繁琐&#xff0c;…

仪器校准的概念与定义,计量校准是什么?

仪器校准的定义&#xff0c;在之前所颁布的《国际计量学词汇 基础和通用概念及相关术语》文件中&#xff0c;已经有了明确说明&#xff0c;而该文件做了修改以后&#xff0c;在后续新的定义中&#xff0c;仪器校准具体被分为两部分&#xff0c;第一步是将被计量仪器和计量校准的…

汽车制造企业中MES管理系统还有哪些作用

在当今汽车制造业的飞速发展中&#xff0c;数字化转型已成为企业不可或缺的战略选择。在这个转型浪潮中&#xff0c;MES管理系统扮演着至关重要的角色&#xff0c;成为连接企业资源计划&#xff08;ERP&#xff09;与车间自动化系统的关键纽带。它不仅推动了生产流程的智能化、…

SpringSecurity中文文档(Servlet Persisting Authentication)

Persisting Authentication 用户第一次请求受保护的资源时&#xff0c;系统会提示他们输入凭据。提示凭据的最常见方法之一是将用户重定向到登录页。对于请求受保护资源的未经身份验证的用户&#xff0c;总结的 HTTP 交换可能如下所示: Example 1. Unauthenticated User Requ…