文章目录

  • 解法1:保存祖先节点+逐个判断
  • 解法2:深度优先遍历
  • 解法3:记录祖先节点

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

难度:中等


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

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

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

示例 1:

  输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1

  输出: 3

  解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2:

  输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4

  输出: 5

  解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:

  • 所有节点的值都是唯一的。

  • p、q 为不同节点且均存在于给定的二叉树中。

解法1:保存祖先节点+逐个判断

  一个很直的方法就是对树进行深度优先遍历,并在遍历过程中使用一个栈保存遇到的节点,直到遍历到节点 p 或节点 q 为止(包括这个节点)。

  此时,栈中保存了 pq 的所有祖先节点,将栈中节点一个个出栈,对每个节点判断其子树中是否 包含另一个节点(若栈中保存的是 p 的祖先节点,则判断是否包含 q ,反之亦然),当遇到第一个包含另一个节点的祖先节点,这个节点就是最近的公共祖先节点。

JS 代码:

var lowestCommonAncestor = function(root, p, q) {let stack = [];let flag = false;let tmp;// 对树进行深度优先遍历,保存路径上的节点const dfs = (root) => {if (flag || !root) {return;}stack.push(root);if (root === p || root === q) {flag = true; // 当找到其中一条路径后,可提前退出tmp = root;return;}dfs(root.left);dfs(root.right);if (!flag) {stack.pop();}}; dfs(root);flag = false;// 对每个节点判断子树中是否包含另一个节点const check = (root) => {if (flag || !root) {return;}if ((root === p || root === q) && root !== tmp) {flag = true;return;}check(root.left);check(root.right);};for (let i = stack.length-1; i >= 0; i--) {check(stack[i]);if (flag) {return stack[i];}}
};

  上面的代码中,对树的深度优先遍历可替换为后序非递归遍历,这样的话就不用考虑非祖先路径上的节点问题了。

解法2:深度优先遍历

  解法2来源于 LeetCode ,主要思路是在遍历过程中根据左右子树是否包含了 p 节点或 q 节点来寻找祖先节点。

  即根据逻辑表达式:(lson && rson) || ((root.val === p.val || root.val === q.val) && (lson || rson)) 来进行判断当前节点 root 是否为祖先节点。

  该表达式包括了两种情况,lson 表示左子树中是否包含 pq 节点,rson 表示右子树中是否包含 pq 节点:

  • lson && rson 表示左右子树中包含了 p 节点和 q 节点,这两个节点是在不同的子树中
  • (root.val === p.val || root.val === q.val) && (lson || rson) 表示当前节点为 pq 节点的情况,且此时当前节点的子树中包含了另一个节点(因为两个节点值是不同的,所以只要 lsonrson 中有一个为真就代表了包含了另一个节点)。

JS 代码如下,其中 flag 用于提前退出递归:

var lowestCommonAncestor = function(root, p, q) {let ans, flag = false;const dfs = (root) => {if (!root || flag) {return false;}let lson = dfs(root.left);let rson = dfs(root.right);if ((lson && rson) || ((root.val === p.val || root.val === q.val) && (lson || rson))) {ans = root;flag = true;return true;}return lson || rson || root.val === p.val || root.val === q.val;}dfs(root);return ans;
};

解法3:记录祖先节点

  解法 3 来源于 LeetCode

  解法 3 中的记录祖先节点和解法 1 中的不同,解法 1 是使用数组顺序记录了 pq 其中一个节点中的祖先节点路径,而解法 3 中则是以节点值为索引来将祖先节点保存在数组中,这样,我们就能根据 pq 节点的值往上跳来找到公共的祖先节点。

  具体做法是遍历这两个节点的祖先节点,并使用一个数组 vis 来标识某个节点是否被访问过,若我们先遍历 p 的祖先节点,则在遍历 q 的祖先节点时,遇到的第一个被访问过的祖先节点就是它们的最近公共祖先节点。

JS 代码:

var lowestCommonAncestor = function(root, p, q) {let ans;let fa = [];let vis = [];fa[root.val] = false;const dfs = (root) => {if (!root) {return;}if (root.left) {fa[root.left.val] = root;dfs(root.left);}if (root.right) {fa[root.right.val] = root;dfs(root.right);}};dfs(root);while (p) {vis[p.val] = true;p = fa[p.val];}while (q) {if (vis[q.val]) {return q;}q = fa[q.val];}
};

LeetCode 236. 二叉树的最近公共祖先相关推荐

  1. 力扣(LeetCode)236. 二叉树的最近公共祖先(C语言)

    一.环境说明 本文是 LeetCode 236. 二叉树的最近公共祖先,使用c语言实现. 递归. 测试环境:Visual Studio 2019. 二.代码展示 精简代码: struct TreeNo ...

  2. leetcode 236. 二叉树的最近公共祖先 递归解法 c语言

    如题: 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先.百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x, 满足 x 是 p.q ...

  3. 最近公共祖先_[LeetCode] 236. 二叉树的最近公共祖先

    题目链接: https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree 难度:中等 通过率:57.2% 题目描述: ...

  4. leetcode 236. 二叉树的最近公共祖先LCA(后序遍历,回溯)

    LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. 题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先.百度百科 ...

  5. Leetcode 236.二叉树的最近公共祖先

    Time: 20190907 Type: Medium 题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p. ...

  6. [leetcode]236.二叉树的最近公共祖先

    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个节点 p.q,最近公共祖先表示为一个节点 x,满足 x 是 p.q 的祖先且 ...

  7. leetcode 236. 二叉树的最近公共祖先 思考分析

    目录 题目 思考分析 改进 本文章代码思路来源于公众号[代码随想录] 题目 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个 ...

  8. LeetCode 236. 二叉树的最近公共祖先(递归)

    题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q ...

  9. 【LeetCode】【HOT】236. 二叉树的最近公共祖先(递归)

    [LeetCode][HOT]236. 二叉树的最近公共祖先 文章目录 [LeetCode][HOT]236. 二叉树的最近公共祖先 package hot;class TreeNode{int va ...

最新文章

  1. SDWebImage使用——一个可管理远程图片加载的类库
  2. WPF实用指南二:移除窗体的图标
  3. boost::geometry::append用法的测试程序
  4. datetime类型的取年月日 sql_SQL2005怎么截取datetime类型字段的年月日,并以截取后的(年月日)字段排序...
  5. 获取当前周和前一周周一和周天,下一周周一和周天
  6. CTreeCtrl 类的InsertItem成员函数
  7. IC中的RDC中存在的亚稳态风险以及解决方案
  8. 微信公众号——分享给朋友/分享至朋友圈(Vue)
  9. 以5‰的概率计算一个网络准确率达到99.9%的时间和迭代次数---实例三分类mnist 3,4,5
  10. 号外号外!兹有第一届区块链技术及应用峰会(BTA)·中国首轮议题抢鲜看
  11. 大众车机天宝187A Hack笔记
  12. 【SequoiaDB 学习笔记】巨杉分布式数据库初接触
  13. 如何区别计算机体系结构与计算机组成这两个概念?
  14. LinuxShell宝典
  15. js 格式化,过万转换成万(W),过亿转化成亿(M)
  16. mysql st_contains实现_查看某一个点是否在某个多边形内 使用ST_Contains函数
  17. mpp region osd反色
  18. 需求分解 设计_分解设计专业知识
  19. ElasticSearch 拼音插件elasticsearch-analysis-pinyin + IK 分词器
  20. IDEA常用快捷键,提升开发效率

热门文章

  1. 解决存储过程中数据安全问题的四种方式
  2. 使用堆内内存HeapByteBuffer的注意事项
  3. vivo 互联网业务就近路由技术实战
  4. Docker不再是唯一的选择
  5. 同样是持久化,竟然有这么大的差别!
  6. 一文读懂jar包的小秘密
  7. Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控...
  8. 五年程序员败在阿里三面,还是Java底层原理的问题啊!
  9. Kafka主题体系架构-复制、故障转移和并行处理
  10. 每个程序员都应该知道的10件事!