【LeetCode】236. 二叉树的最近公共祖先

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

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 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。因为根据定义最近公共祖先节点可以为节点本身。

二、解决

1、暴力之哈希迭代

思路: 直观思路是由p、q出发去寻找父节点,该路径相交的最近节点是最近公共祖先。

但这题没有双向指针,不过父子关系可以用Map<root.child.val, root>来存储,把root到所有节点的路径都存下来,其中包含root–>p的path1与root–>q的path2。

然后遍历path1,由p.val出发,到root结束,把沿途所有val存到Set<Integer>中;然后再遍历path2,从q.val出发,判断Set()中是否包含沿途节点的val,如果包含,则说明该节点是最近公共祖先。

可能有点绕,举个例子:
例子

: 节点23的最近公共祖先。

Step1: 存下父子关系。<p.val, root> and <q.val, root>,即<2, 1> and <3, 1>。
Step2.1: 由p出发,访问从p-->root的沿途所有节点的值,并存入set中。
         在这是把21存入set, 代表访问了,即set(2, 1)。
Step2.2: 再从q出发,访问q-->root的沿途所有节点的值,判断节点值在set()中是否存在,存在,则说明该节点是最近公共祖先。
         在这是,先看3,没在set(2,1)中,再看1,在其中,说明123的最近公共祖先。
Step3: 返回节点1

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    
    
    Map<Integer, TreeNode> parent = new HashMap<Integer, TreeNode>();
    Set<Integer> visited = new HashSet<Integer>();

    public void dfs(TreeNode root) {
    
    
        if (root.left != null) {
    
    
            parent.put(root.left.val, root);
            dfs(root.left);
        }
        if (root.right != null) {
    
    
            parent.put(root.right.val, root);
            dfs(root.right);
        }
    }

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    
    
        dfs(root);
        while (p != null) {
    
    
            visited.add(p.val);
            p = parent.get(p.val);
        }
        while (q != null) {
    
    
            if (visited.contains(q.val)) {
    
    
                return q;
            }
            q = parent.get(q.val);
        }
        return null;
    }
}

时间复杂度: O(n)
空间复杂度: O(n)

扫描二维码关注公众号,回复: 12059871 查看本文章

2、递归

思路: 跳出来一点,从左、右子树的宏观视角去思考问题。

从根节点出发,在左右子树中去寻找包含节点p或q,如果返回结果同时不为空,则说明该root是最近公共祖先。

递归有点不太好理解,建议自己用上面的例子1-[2,3],自己推演下。

代码:

class Solution {
    
    
	// 作用:以root为根的二叉树,去子树中寻找p/q
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    
    
       // 为空或存在一个,就返回那个节点p/q.
        if(root == null || root == p || root == q)  return root;
        // root!=null && root!=p && root!=q, 就继续去左右子树中去寻找
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        // 如果左右子树均不为空,说明找到了,则返回root
        if(left != null && right != null)   return root;
		// 如果有一个为空,说明返回为空的那个子树不包含p/q节点。我们就返回非空的那个子树,继续向下寻找
        return left != null ? left : right;
    }
}

时间复杂度: O(n)
空间复杂度: O(n)

三、参考

1、My Java Solution which is easy to understand
2、Java/Python iterative solution
3、Java iterative and recursive solutions.
4、二叉树的最近公共祖先

猜你喜欢

转载自blog.csdn.net/HeavenDan/article/details/108310866