当前位置: 首页 > news >正文

[M二叉树] lc236. 二叉树的最近公共祖先(dfs+二叉搜索树)

文章目录

    • 1. 题目来源
    • 2. 题目解析

1. 题目来源

链接:236. 二叉树的最近公共祖先

相似题:

  • [M二叉树] lc235. 二叉搜索树的最近公共祖先(dfs+二叉搜索树)

题单:

  • 【题单】链表、二叉树与一般树(前后指针/快慢指针/DFS/BFS/直径/LCA)
    • 二、二叉树
      • §2.8 最近公共祖先

2. 题目解析

很经典的题目哈,二刷的时候,再注意下非递归写法吧。

思路:

  • 本题没有 BST 树这样好的性质,没有办法去确定到底去左边搜、还是右边搜。
  • 故,那就两边都来搜一下就行了。看看 最近公共祖先 的本质是什么?
    • 本质:以当前节点为根节点所处的子树中,p,q 同时出现。那么当前节点即为 p,q 的最近公共祖先。
  • 所以,我们需要一个 dfs 函数,它能获取到以当前 root 为节点的子树中,p、q 的出现情况。
  • 这里也是借用了 先递归、再回溯,当遇见的第一个满足的节点,就一定是 最近的 公共祖先。

灵神的题解这个写的非常好,代码实现很简洁,dfs 返回值再考虑了多种情况下,合并了多种情况。值得思考下。

同时思考一个问题:

  • 本题是最近公用祖先,但同时也有其他的公共祖先存在。
  • 在 y总 的写法中是用了全局变量,配合递归回溯的时候,进行了首次的判断,后续的其他公共祖先就不会记录答案。
  • 灵神这里直接这样写,不会受到其他公共祖先的影响吗?
    • 其实不会。
    • 以 left && right 成立时,root 为最近公共祖先为例。
    • 此时回溯上去,它是从 r1->left 或者 r1->right 来的。此时,其中一个分支肯定是不会包含 p、q 的,所以这个分支的回溯结果是 null。
    • 故,假设 r1->left 这个分支返回的结果就是 root。则 r1->right 这个分支返回的结果就是 null。
    • 所以进不去 left&&right,说明 root 并非 p、q 的最近公共祖先。所以上层的这个公共祖先,实际上不会被返回。
    • 然后判断 left 非空,说明在 left 已经找到了最近公共祖先,且公共祖先的位置是 left。继续向上返回即可。
    • 综上,其他的最近公共祖先,都不会被返回。能返回的只有最近的公共祖先。故此,将被层层返回到顶层,作为答案。

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( 1 ) O(1) O(1)

递归写法:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:TreeNode* res = NULL;int dfs(TreeNode* root, TreeNode* p, TreeNode* q) {if (!root) return 0;int st = 0;if (root == p) st |= 1;  // 看看根节点是不是 pelse if (root == q) st |= 2; // 看看根节点是不是 qst |= dfs(root->left, p, q); // 看看左子树中包含 p、q 的情况st |= dfs(root->right, p, q); // 看看右子树中包含 p、q 的情况if (st == 3 && !res) res = root; // 回溯到根节点时,如果根节点子树中包含了 p, q 且 res 未被赋值,那就是最近公共祖先return st;}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {dfs(root, p, q);return res;}
};

包括这样写也是可以的:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:TreeNode* res = NULL;int dfs(TreeNode* root, TreeNode* p, TreeNode* q) {if (!root) return 0;int st = dfs(root->left, p, q);if (root == p) st |= 1;     // 这个状态转移,放这里,放下面都是可以的else if (root == q) st |= 2;st |= dfs(root->right, p, q);// if (root == p) st |= 1;     // 放这里也是可以的,只要遍历到了每个节点都行// else if (root == q) st |= 2;if (st == 3 && !res) res = root;return st;}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {dfs(root, p, q);return res;}
};

灵神的简洁写法:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if (!root || root->val == p->val || root->val == q->val) return root; // 最近公共祖先位置、p、q 位置、null 位置auto left = lowestCommonAncestor(root->left, p, q);auto right = lowestCommonAncestor(root->right, p, q);if (left && right) return root;     // 最近公共祖先位置// 这里就保证了,如果在下层的 root 节点找到其为最近公共祖先的话// 就会层层返回上去,而非用上面的 root 值进行覆盖。// 所以,在这里不需要开辟外部的全局变量进行「首次遇见最近公共祖先」的这个记录return left ? left : right;}
};

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • IEEE P3233 标准启动会回顾:迈向去中心化存储标准化的第一步
  • 华为云CCE集群创建loadBalancer
  • 如何使用mcu 内置 flash 实现fatfs
  • jQuery基础——DOM
  • 要隐藏滚动条,您可以使用CSS中的overflow属性。
  • 编译可执行命令的FFmpeg
  • 达达熊搭建工程日志(持续更新中)
  • 【Python报错已解决】`ModuleNotFoundError: No module named ‘graphviz‘`
  • java利用JXL操作excel
  • 自然语言处理系列四十八》Word2vec词向量模型》算法原理
  • MyBatis 源码解析:SqlSessionFactoryBuilder 解析与实现
  • EasyExcel动态映射Excel数据到任意实体类教程
  • 会议记录|MAS Lab 年度组会记录
  • 低代码开发与数据库:数字化转型的新引擎
  • SpringBoot 集成 kafka
  • ➹使用webpack配置多页面应用(MPA)
  • bearychat的java client
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • Go 语言编译器的 //go: 详解
  • java正则表式的使用
  • MobX
  • npx命令介绍
  • react-native 安卓真机环境搭建
  • SQLServer之创建数据库快照
  • 订阅Forge Viewer所有的事件
  • 仿天猫超市收藏抛物线动画工具库
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 检测对象或数组
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 什么是Javascript函数节流?
  • 小程序01:wepy框架整合iview webapp UI
  • 原生Ajax
  • 自动记录MySQL慢查询快照脚本
  • ​secrets --- 生成管理密码的安全随机数​
  • ​比特币大跌的 2 个原因
  • ​十个常见的 Python 脚本 (详细介绍 + 代码举例)
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #162 (Div. 2)
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (C语言)fgets与fputs函数详解
  • (C语言)二分查找 超详细
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (简单) HDU 2612 Find a way,BFS。
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (一)SvelteKit教程:hello world
  • (一)utf8mb4_general_ci 和 utf8mb4_unicode_ci 适用排序和比较规则场景
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)linux 命令大全
  • (转)winform之ListView
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .NET 8.0 中有哪些新的变化?
  • .Net Core中Quartz的使用方法
  • .NET Framework .NET Core与 .NET 的区别
  • .Net(C#)常用转换byte转uint32、byte转float等