LeetCode 865. Smallest Subtree with all the Deepest Nodes【树,DFS,BFS,哈希表】1534

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

给定一个根为 root 的二叉树,每个节点的深度是 该节点到根的最短距离

返回包含原始树中所有 最深节点最小子树

如果一个节点在 整个树 的任意节点之间具有最大的深度,则该节点是 最深的

一个节点的 子树 是该节点加上它的所有后代的集合。

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4]
输出:[2,7,4]
解释:
我们返回值为 2 的节点,在图中用黄色标记。
在图中用蓝色标记的是树的最深的节点。
注意,节点 532 包含树中最深的节点,但节点 2 的子树最小,因此我们返回它。

示例 2:

输入:root = [1]
输出:[1]
解释:根节点是树中最深的节点。

示例 3:

输入:root = [0,1,3,null,2]
输出:[2]
解释:树中最深的节点为 2 ,有效子树为节点 210 的子树,但节点 2 的子树最小。

提示:

  • 树中节点的数量在 [1, 500] 范围内。
  • 0 <= Node.val <= 500
  • 每个节点的值都是 独一无二 的。

注意: 本题与力扣 1123 重复:https://leetcode-cn.com/problems/lowest-common-ancestor-of-deepest-leaves


解法1 递归


看上图(示例 1),这棵树的节点 3 , 5 , 2 3,5,2 3,5,2 都是最深叶节点 7 , 4 7,4 7,4 的公共祖先,但只有节点 2 2 2 是最近的公共祖先。

如果我们要找的节点只在左子树中,那么最近公共祖先也必然只在左子树中。对于本题,如果左子树的最大深度比右子树的大,那么最深叶结点就只在左子树中,所以最近公共祖先也只在左子树中。反过来说,如果右子树的最大深度大于左子树,那么最深叶结点就只在右子树中,所以最近公共祖先也只在右子树中。

如果左右子树的最大深度一样呢?当前节点一定是最近公共祖先吗?不一定。比如节点 1 1 1 的左右子树最深叶节点 0 , 8 0,8 0,8 的深度都是 2 2 2 ,但该深度并不是全局最大深度,所以节点 1 1 1 并不能是答案。

根据以上讨论,正确做法如下:

  • 递归这棵二叉树,同时维护全局最大深度 maxDepth \textit{maxDepth} maxDepth
  • 在「」的时候往下传 d e p t h depth depth ,用来表示当前节点的深度
  • 在「」的时候往上传当前子树最深叶节点的深度
  • 设左子树最深叶节点的深度为 leftMaxDepth \textit{leftMaxDepth} leftMaxDepth ,右子树最深叶节点的深度为 rightMaxDepth \textit{rightMaxDepth} rightMaxDepth 。如果 leftMaxDepth = rightMaxDepth = maxDepth \textit{leftMaxDepth}=\textit{rightMaxDepth}=\textit{maxDepth} leftMaxDepth=rightMaxDepth=maxDepth ,那么更新答案为当前节点。注意这并不代表我们找到了答案,如果后面发现了更深的叶节点,那么答案还会更新。
class Solution {
public:TreeNode *subtreeWithAllDeepest(TreeNode *root) {TreeNode *ans = nullptr;int max_depth = -1; // 全局最大深度function<int(TreeNode*, int)> dfs = [&](TreeNode *node, int depth) {if (node == nullptr) {max_depth = max(max_depth, depth); // 维护全局最大深度return depth;}int left_max_depth = dfs(node->left, depth + 1); // 获取左子树最深叶节点的深度int right_max_depth = dfs(node->right, depth + 1); // 获取右子树最深叶节点的深度if (left_max_depth == right_max_depth && left_max_depth == max_depth)ans = node;return max(left_max_depth, right_max_depth); // 当前子树最深叶节点的深度};dfs(root, 0);return ans;}
};

复杂度分析:

  • 时间复杂度: O ( n ) \mathcal{O}(n) O(n) 。每个节点都会恰好访问一次。
  • 空间复杂度: O ( n ) \mathcal{O}(n) O(n) 。最坏情况下,二叉树是一条链,递归需要 O(n)\mathcal{O}(n)O(n) 的栈空间。

解法2 自底向上

也可以不用全局变量,而是把每棵子树都看成是一个「子问题」,即对于每棵子树,我们需要知道:

  • 这棵子树最深叶结点的深度。这里是指叶子在这棵子树内的深度,而不是在整棵二叉树的视角下的深度。相当于这棵子树的高度
  • 这棵子树的最深叶结点的最近公共祖先 lca \textit{lca} lca

分类讨论:

  • 设子树的根节点为 n o d e node node n o d e node node 的左子树的高度为 leftHeight \textit{leftHeight} leftHeight n o d e node node 的右子树的高度为 rightHeight \textit{rightHeight} rightHeight
  • 如果 l e f t H e i g h t > r i g h t H e i g h t leftHeight>rightHeight leftHeight>rightHeight ,那么子树的高度为 leftHeight + 1 \textit{leftHeight} + 1 leftHeight+1 lca \textit{lca} lca 是左子树的 lca \textit{lca} lca
  • 如果 leftHeight < rightHeight \textit{leftHeight} < \textit{rightHeight} leftHeight<rightHeight ,那么子树的高度为 r i g h t H e i g h t + 1 rightHeight+1 rightHeight+1 l c a lca lca 是右子树的 l c a lca lca
  • 如果 leftHeight = rightHeight \textit{leftHeight} = \textit{rightHeight} leftHeight=rightHeight ,那么子树的高度为 leftHeight + 1 \textit{leftHeight} + 1 leftHeight+1 l c a lca lca 就是 n o d e node node 。反证法:如果 l c a lca lca 在左子树中,那么 l c a lca lca 不是右子树的最深叶结点的祖先,这不对;如果 l c a lca lca 在右子树中,那么 l c a lca lca 不是左子树的最深叶结点的祖先,这也不对;如果 l c a lca lca n o d e node node 的上面,那就不符合「最近」的要求。所以 l c a lca lca 只能是 n o d e node node
class Solution {pair<int, TreeNode*> dfs(TreeNode *node) {if (node == nullptr)return {0, nullptr};auto [left_height, left_lca] = dfs(node->left);auto [right_height, right_lca] = dfs(node->right);if (left_height > right_height) // 左子树更高return {left_height + 1, left_lca};if (left_height < right_height) // 右子树更高return {right_height + 1, right_lca};return {left_height + 1, node}; // 一样高}public:TreeNode *subtreeWithAllDeepest(TreeNode *root) {return dfs(root).second;}
};

复杂度分析:

  • 时间复杂度: O ( n ) \mathcal{O}(n) O(n) 。每个节点都会恰好访问一次。
  • 空间复杂度: O ( n ) \mathcal{O}(n) O(n) 。最坏情况下,二叉树是一条链,递归需要 O ( n ) \mathcal{O}(n) O(n) 的栈空间。

更简洁的写法是:

class Solution {
public:int depth[1010];TreeNode* subtreeWithAllDeepest(TreeNode* root) {if (root == nullptr) return nullptr;TreeNode* left = root->left, *right = root->right;TreeNode* lcaLeft = subtreeWithAllDeepest(root->left), *lcaRight = subtreeWithAllDeepest(root->right);int dl = left ? depth[left->val] : 0, dr = right ? depth[right->val] : 0;depth[root->val] = max(dl, dr) + 1;if (dl > dr) return lcaLeft;if (dr > dl) return lcaRight;return root;}
};

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

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

相关文章

机械零件保养3d模拟演示打消客户购买顾虑

复杂机械的工作运转是复杂的&#xff0c;想要对机械有深度的理解和迭代&#xff0c;必须了解它的运转原理及参数&#xff0c;复杂机械运行原因教学存在着不可视、系统庞杂及知识点多等弊病&#xff0c;3D虚拟展示是基于web3d网页运行的三维页面&#xff0c;可以将复杂机械运行过…

Java复习-20-接口(3)- 代理设计模式

代理设计模式(Proxy) 功能&#xff1a;可以帮助用户将所有的开发注意力只集中在核心业务功能的处理上。 代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身&#xff0c;以控制对这个对象的访问。即通过代理对象访问目标目标对象&#xff0c;可以在目…

uni-app开发小程序时ucharts图表如何使用

在此不会具体告诉大家怎么做&#xff0c;我只告诉大家方法&#xff1a; 第一步&#xff0c;推荐使用组件方法进行绘图&#xff0c;首先去官网下载这个ucharts的插件&#xff1a; 秋云 ucharts echarts 高性能跨全端图表组件 - DCloud 插件市场 下载完毕导入到所需要用到的项目…

数据结构与算法基础-学习-33-归并排序

目录 一、基本思想 二、算法思路 1、合并两个有序序列 2、分治法 三、算法源码 1、MergeSortTwoSortData 2、TwoWayMergeSortRecurtionSentryQueue 四、算法效率分析 五、Linux环境编译测试 六、小感慨 排序的其他相关知识点和源码分享可以参考之前的博客&#xff1a…

Docker入门,Docker是什么?有什么用?该怎么用?

目录 1. 项目部署时的复杂性&#xff1f; 2. Docker是如何解决依赖兼容问题的&#xff1f; 3. 众多Linux操作系统发行版的区别 4. Docker 是如何实现跨系统运行的&#xff1f; 5. Docker与虚拟机的差别 6. 镜像(Image)与容器(Container) 7. DockerHub 8. Docker 架构 …

安防监控/视频汇聚/云存储/AI智能视频分析平台EasyCVR显示CPU过载,该如何解决?

视频云存储/安防监控/视频汇聚平台EasyCVR基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防视频监控系统EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云…

Java知识总结(持续更新)

一、JDK、JRE、JVM三者之间的关系&#xff1f; 1. **JDK (Java Development Kit)**&#xff1a; JDK 是 Java 开发工具包&#xff0c;它包含了用于开发 Java 应用程序的所有必要工具和库。这包括 Java 编译器&#xff08;javac&#xff09;、Java 核心类库、开发工具&#x…

《React vs. Vue vs. Angular:2023年的全面比较》

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【LeetCode-简单题】844. 比较含退格的字符串

文章目录 题目方法一&#xff1a;单指针方法二&#xff1a;双指针方法三&#xff1a;栈 题目 方法一&#xff1a;单指针 首先每次进入循环处理之前需要对第一个字符进行判断&#xff0c;若是退格符&#xff0c;直接删掉&#xff0c;结束此次循环fast从0开始&#xff0c;如果fa…

每日一练 | 网络工程师软考真题Day32

阅读以下说明&#xff0c;答复以下【问题1】至【问题5】 【说明】 某公司内部效劳器S1部署了重要的应用&#xff0c;该应用只允许特权终端PC1访问&#xff0c;如图4-1所示。为保证通信平安&#xff0c;需要在S1上配置相应的IPSec策略。综合考虑后&#xff0c;确定该IPSec策略如…

pdf.js 微信公众号不显示问题

问题1&#xff1a; 在浏览器中能够正常显示&#xff0c; 但是在微信浏览器中不行&#xff01;解决&#xff1a; 这个是pdf.js 版本问题&#xff0c; 用2.4版本&#xff0c;微信打开就没问题了 问题2&#xff1a; 如何关闭侧边栏&#xff1f; 修改这个地方&#xff0c; 将 -1 改…

QLineEdit 类(行编辑器)

1、 QLineEdit 类是 QWidget 类的直接子类&#xff0c;该类实现了一个单行的输入部件&#xff0c;即行编辑器&#xff1b; 2、验证器(QValidator 类)和输入掩码简介&#xff1a;主要作用是验证用户输入的字符是否符合验证器 的要求&#xff0c;即限制对用户的输入&#xff0c;比…

发UPS国际快递到墨西哥的收费标准

UPS国际快递是目前全球范围内最为知名和可靠的快递服务提供商之一&#xff0c;无论是个人还是企业都可以通过UPS将包裹快速送达世界各地&#xff0c;其中包括墨西哥。所以&#xff0c;对于许多人来说&#xff0c;了解到发UPS国际快递到墨西哥的收费标准是十分重要的。 发UPS国际…

Linux常见进程类别

目录 常见进程类别 守护进程&精灵进程 任务管理 进程组 作业 作业 | 进程组 会话 w命令 守护进程 守护进程的创建 setsid()函数 daemon()函数 模拟实现daemon函数 前台进程 | 后台进程 僵尸进程 | 孤儿进程 僵尸进程的一些细节 守护进程 | 后台进程 守护…

C++-map和set

本期我们来学习map和set 目录 关联式容器 键值对 pair 树形结构的关联式容器 set multiset map multimap 关联式容器 我们已经接触过 STL 中的部分容器&#xff0c;比如&#xff1a; vector 、 list 、 deque 、forward_list(C11)等&#xff0c;这些容器统称为序列式…

关于软件的功能复用

有一些人总在说软件要复用&#xff0c;开发一个项目时要想想怎么在另一个项目中能重用。你问他怎么做到复用&#xff0c;就会听到微服务、中台一些名词 复用的层次 说到复用&#xff0c;首先要想明白复用的是啥 级别越低&#xff0c;粒度越小&#xff0c;复用的范围越广&#…

Yolov5改进算法之添加Res2Net模块

目录 1. Res2Net介绍 1.1 Res2Net的背景和动机 1.2 Res2Net的基本概念 2. YOLOV5添加Res2Net模块 Res2Net&#xff08;Residual Resolution Network&#xff09;是一种用于图像处理和计算机视觉任务的深度卷积神经网络架构。它旨在解决传统的ResNet&#xff08;Residual Ne…

【java】【SSM框架系列】【三】Maven进阶

目录 一、分模块开发与设计 1.1 分模块开发的意义 1.2 分模块开发&#xff08;模块拆分&#xff09; 二、依赖管理 2.1 依赖传递 2.2 可选依赖 2.3 排除依赖 三、聚合与继承 3.1 聚合 3.2 继承 3.3 聚合与继承的区别 四、属性管理 4.1 属性 4.1.1 属性配置与使用 …

2023 年高教社杯全国大学生数学建模竞赛题目 B 题 多波束测线问题

B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术。声波在均匀介质中作匀速直线传播&#xff0c;在不同界面上产生反射&#xff0c;利用这一原理&#xff0c;从测量船换能器垂直向海底发射声波信号&#xff0c;并记录从声波发射到信号接收的传播…

智慧工地:让工地可视化、数字化、智能化

智慧工地平台功能包括&#xff1a;劳务管理、施工安全管理、视频监控管理、机械安全管理、危大工程监管、现场物料监管、绿色文明施工、安全隐患排查、施工综合管理、施工质量管理、设备管理、系统管理等模块。 一、项目开发环境 技术架构&#xff1a;微服务 开发语言&#…