Algorithm Research - nearest common ancestor of binary tree (O (n))_ two hundred and thirty-six

1, Title Description

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

The recent definition of public ancestor in Baidu Encyclopedia is: "for rooted trees T Two nodes of p,q,The nearest common ancestor is represented as a node x,satisfy x yes p,q Our ancestors and x As deep as possible (a node can also be its own ancestor)

 

Example 1:


Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
 Output: 3
 Explanation: the nearest common ancestor of node 5 and node 1 is node 3.
Example 2:


Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
 Output: 5
 Explanation: the nearest common ancestor of node 5 and node 4 is node 5. Because according to the definition, the nearest common ancestor node can be the node itself.
Example 3:

Input: root = [1,2], p = 1, q = 2
 Output: 1
 

Tips:

The number of nodes in the tree is in the range [2, 105] Inside.
-109 <= Node.val <= 109
 All Node.val Different from each other.
p != q
p and q All exist in a given binary tree.

Source: force buckle( LeetCode)
Link: https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree
 The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

2, Train of thought

Recently, when I was brushing questions, I suddenly found that it is a relatively easy way to learn and absorb excellent experience. Therefore, if I encounter good ideas, I will directly use good ideas and schemes, which is not my own.

The following ideas come from the great God. Let's learn:

Definition of ancestor: if node p is in the left (right) subtree of node root, or p=root, root is said to be the ancestor of p.

Before skimming the questions, we mentioned in the introduction to data structure that we clearly call a data structure binary tree:

Binary tree

Binary tree is a special form of tree. Binary means that each node of this tree has at most 2 child nodes (it can be none or 1 child node)

Now let's look at another picture

Definition of nearest common ancestor: set node root as a common ancestor of nodes P and Q. if its left child node root.left and right child node root.right are not the common ancestors of P and Q, root is called "nearest common ancestor".

According to the above definition, if root is the nearest common ancestor of P and Q, it can only be one of the following cases:

p and q are in the subtree of root, and are on the opposite side of root (i.e. in the left and right subtrees respectively);

p = root, and q is in the left or right subtree of root;

q = root, and p is in the left or right subtree of root;

Consider traversing the binary tree through recursion, and return when node p or q is encountered. Backtracking from bottom to top, when node p and Q are on the opposite side of node root, node root is the nearest common ancestor, and returns root upward.

The very valuable learning is that when we deal with recursion, we need to extract the termination conditions, the content of recursion and the confirmation of the return value of each recursion. The following is a picture explanation








Complexity analysis:

Time complexity O(N): where N is the number of nodes of the binary tree; in the worst case, all nodes of the tree need to be traversed recursively.

Space complexity O(N): in the worst case, the recursion depth reaches N, and the system uses additional space of O(N) size.

3, Code implementation

/**
     * Define a binary tree with left and right subtrees
     */
     public class TreeNode {
      int val;
      TreeNode left;
      TreeNode right;
      TreeNode(int x) { val = x; }
     }

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //The first step is to deal with the termination conditions in the recursive process, which are divided into three categories: root=null, root=p and root=q
        if(root == null || root ==p || root == q){
            return root;
        }
        //Step 2: if the root node is not a public parent node, we need to find it according to the left subtree and the right subtree
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        //Step 3: we need to judge that if the left subtree is null, we will directly return to the right subtree. If the right subtree is null, we will return to the left subtree
        //Step 4. If the left and right subtrees are not null, we think that root is the smallest public parent node
        return left == null ? right : right == null ? left : root;
    }

4, Summary

There are other solutions. For example, I think a good idea is that we use depth first search. The first step is to find the parent node of P, and then maintain the parent nodes corresponding to the child nodes in the dictionary map one by one from the bottom, and then we continue the depth first search. If we find that the parent node of Q is equal to the parent node of P, it is the smallest public parent node. If we don't find it Returns null if.

The code is also well understood, as follows

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

Complexity analysis

Time complexity: O(N)O(N), where NN is the number of nodes of the binary tree. All nodes of the binary tree have and will only be accessed once. The number of ancestor nodes jumping up from p and q nodes will not exceed NN, so the total time complexity is O(N)O(N).

Spatial complexity: O(N)O(N), where NN is the number of nodes of the binary tree. The stack depth of recursive calls depends on the height of the binary tree. In the worst case, the binary tree is a chain, and the height is NN, so the spatial complexity is O(N)O(N). The hash table stores the spatial complexity of O(N)O(N) for the parent node of each node, so the final total spatial complexity is O(N)O(N).

Author: leetcode solution

Link: https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/solution/er-cha-shu-de-zui-jin-gong-gong-zu-xian-6fdt7/

Source: LeetCode

The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Tags: Python Algorithm data structure

Posted on Fri, 19 Nov 2021 13:15:50 -0500 by eyekkon