Given a binary tree, find the nearest common ancestor of two specified nodes in the 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 。因为根据定义最近公共祖先节点可以为节点本身。

Example 3:

输入:root = [1,2], p = 1, q = 2
输出:1

Problem solving ideas

First of all, the function signature of this problem is as follows:

TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q);

root node identifies a binary tree, p and q are two nodes on this binary tree, allowing you to return the nearest common ancestor of the p node and the q node.

The routines of all binary trees are the same. You can write the traversal framework first:

TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    // 前序遍历
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    // 中序遍历
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    // 后序遍历
}

Now we think about how to add some details and transform the framework into a solution. When encountering any recursive problem, it is nothing more than three questions of the soul:

1. What does this function do?

2. What are the variables in the parameters of this function?

3. What should you do to get the recursive result of the function?

Similar to the idea of dynamic programming, it is also necessary to clearly define "definition", "state" and "choice".

1) What does this function do?

Describe the "definition" of the function lowestCommonAncestor .

Description: Input three parameters root , p , q to this function, it will return a node.

Case 1, if p and q are in the tree rooted at root , the function returns the nearest common ancestor node of p and q .

Case 2, if p nor q are in the tree rooted at root , then null will be returned as a matter of course.

Case 3, if only one of p and q exists in the tree rooted at root , the function returns that node.

The title says that the input p and q must exist in the tree rooted at root , but in the recursive process, the above three situations may occur, so it is necessary to define clearly here, and the subsequent definitions will be reflected in the code

2) Among the parameters of this function, what are the variables?

Describe the "state" of this function.

Description: The variable in the function parameter is root , because according to the framework, lowestCommonAncestor(root) will recursively call root.left and root.right ; as for p and q , we ask for their common ancestor, they will definitely not change, that is, one layer in the recursive process Pass it through layer by layer.

You can also understand that this is "state transition", what is each recursion doing? Isn't it just transferring "root as the root" to "rooting the child nodes of root", and constantly reducing the scale of the problem?

3) Get the recursive result of the function, what should you do?

After getting the result of the recursive call, what "choice" do you make?

Think about the base case first, if root is empty, it must return null . If root itself p or q , for example root is p node it, if q exist in order to root rooted tree, apparently root is the most recent common ancestor; even q does not exist in order to root root of the tree, according to the situation The definition of 3 should also return root node.

The base case of the above two cases can fill in the framework code a little:

TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    // 如果 root 为空
    if (root == null) return null;
    // 如果 root 本身就是 p 或者 q
    if (root == p || root == q) return root;

    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
}

Now do something with the results of the recursive calls left and right . According to the definition of the function in the first question just now, we continue to discuss it by situation:

Case 1, if p and q are in the tree rooted at root , then left and right must be p and q respectively (as seen from the base case).

Case 2, if p nor q are in the tree rooted at root , return null directly.

Case 3, if only one of p and q exists in the tree rooted at root , the function returns that node.

After understanding the above three points, you can directly look at the solution code:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // base case
        if (root == null) return null;
        if (root == p || root == q) return root;

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        // 情况 1
        if (left != null && right !=  null) {
            return root;
        }
        // 情况 2
        if (left == null && right == null) {
            return null;
        }
        // 情况 3
        return left == null ? right : left;
    }
}

Personal understanding:

Search the node at the pre-order position, if it is an empty node, return it directly, if the search p or q , return the node, otherwise continue to recurse

Receive the return value of the pre-order at the post-order position. If left and right are not empty, it means that they are p and q respectively. The current root is the nearest common ancestor, and the root node is returned directly. If one is empty and the other is not empty, it means that a node is found, and the node is passed up to find another node until both are not empty. At this time, root is the nearest common ancestor, and the root node is returned directly.

For case 1, you must have doubts, left and right not empty, they are p and q respectively. It can be said that root is their common ancestor, but can you be sure that root is the "most recent" common ancestor?

This is a clever place, because here is the post-order traversal of the binary tree! pre-order traversal can be understood as from top to bottom, while post-order traversal is from bottom to top, just like starting from p and q and going up, the first intersecting node is this root , you said this is the most recent public What about ancestors?


一杯绿茶
199 声望17 粉丝

人在一起就是过节,心在一起就是团圆