[LeetCode] 236. The nearest common ancestor of the binary tree

Given a binary tree, find the nearest common ancestor of two specified nodes in the tree.

The definition of the nearest common ancestor in Baidu Encyclopedia is: "For two nodes p and q of the rooted tree T, the nearest common ancestor is expressed as a node x, so that x is the ancestor of p and q and the depth of x is as large as possible ( A node can also be its own ancestor )."

For example, given the following binary tree: root = [3,5,1,6,2,0,8,null,null,7,4]
Binary tree

Example 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3

Example 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

Two, solve

1. Brute force hash iteration

Idea: The intuitive idea is to start from p and q to find the parent node, and the closest node that the path intersects is the closest common ancestor.

But there is no two-way pointer in this question, but the parent-child relationship can be stored by Map<root.child.val, root>, and the path from root to all nodes is saved, including root–>p path1 and root–>q path2.

Then traverse path1, starting from p.val, and ending with root, save all vals along the way to Set< Integer >; then traverse path2, starting from q.val, and judge whether Set() contains the val of the nodes along the way, if Include, it means that the node is the nearest common ancestor.

It may be a bit convoluted, for example:
example

: 节点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

Code:

/**
 * 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;
    }
}

Time complexity: O(n)
Space complexity: O(n)

2. Recursion

Idea: Jump out a bit and think about the problem from the macro perspective of the left and right subtrees.

Starting from the root node, look for nodes p or q in the left and right subtrees. If the returned result is not empty at the same time, it means that the root is the nearest common ancestor.

Recursion is a bit difficult to understand. I suggest you use the above example 1-[2,3] and deduct it yourself.

Code:

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;
    }
}

Time complexity: O(n)
Space complexity: O(n)

Three, reference

1. My Java Solution which is easy to understand
2. Java/Python iterative solution
3. Java iterative and recursive solutions.
4. The nearest common ancestor of the binary tree

Guess you like

Origin blog.csdn.net/HeavenDan/article/details/108310866