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

文章目录

    • [1. 题目来源](#1. 题目来源)
    • [2. 题目解析](#2. 题目解析)

1. 题目来源

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

相似题:

题单:

  • 【题单】链表、二叉树与一般树(前后指针/快慢指针/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)

递归写法:

cpp 复制代码
/**
 * 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;  // 看看根节点是不是 p
        else if (root == q) st |= 2; // 看看根节点是不是 q
        st |= 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;
    }
};

包括这样写也是可以的:

cpp 复制代码
/**
 * 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;
    }
};

灵神的简洁写法:

cpp 复制代码
/**
 * 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;
    }
};
相关推荐
XiaoLeisj18 分钟前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝
Jasmine_llq38 分钟前
《 火星人 》
算法·青少年编程·c#
闻缺陷则喜何志丹1 小时前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
Lenyiin1 小时前
01.02、判定是否互为字符重排
算法·leetcode
鸽鸽程序猿1 小时前
【算法】【优选算法】宽搜(BFS)中队列的使用
算法·宽度优先·队列
Jackey_Song_Odd1 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
乐之者v2 小时前
leetCode43.字符串相乘
java·数据结构·算法
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组