Sword finger offer (Second Edition) - the lowest common ancestor of two nodes in the tree

Keywords: Algorithm Interview

PS: Sword finger offer is not only an interview guide that many students will refer to when looking for a job, but also an algorithm Guide (the main reason why it is so popular is that it provides a step-by-step optimization solution, which I think is very friendly). Now many Internet algorithm interview questions can be found here. For the convenience of reference and review in the future, the examples in the book are implemented in Java (Second Edition). You are welcome to communicate and progress together.

GitHub: https://github.com/Uplpw/SwordOffer.

Full title link: https://blog.csdn.net/qq_41866626/article/details/120415258

1 Title Description

Enter the root node of a tree and two observed nodes to find the lowest (nearest) common ancestor of the two nodes.

leetcode link: Lowest common ancestor of binary search tree (the following code has been tested and submitted)
leetcode link: Lowest common ancestor of binary tree (the following code has been tested and submitted)

2 test cases

Generally, functional use cases, special (edge) use cases, counterexamples and invalid test cases are considered. We can even find some rules to solve problems from test cases, and it can also make our program more complete and robust.

(1) Function use case: complete and incomplete binary tree.

(2) Edge use case: only one node, only left node and only right node.

(3) Invalid use case: the tree is empty.

3 ideas

analysis:

There is no clear explanation for the "tree", so the original book makes assumptions about the possible situation of the tree, and then derives a variety of ideas

(1)If binary, search tree:
    Traverse to find nodes larger than the first node and smaller than the second node.
    
(2)For a tree with two-way pointers between parent and child:
    From bottom to top, it is transformed into the problem of finding the first common node of two linked lists.
    
(3)If it's just a normal binary tree with parent-child pointers:

   (3.1)If additional space cannot be used, judge whether its subtree contains those two nodes from the root node and find the smallest subtree
        Time complexity o(n^2),Space complexity is o(1). 
        
   (3.2) If you can use additional space, you can traverse it twice(Depth first)Get the path from the root node to the two nodes, and then find the last public node of the two paths
        Time complexity o(n),Spatial complexity o(logn). 

Ordinary binary tree process:

(1) Termination conditions:

  • When the leaf node is crossed, null is returned directly;
  • When root is equal to p, q, root is returned directly;

(2) Recursive work:

  • Open the recursive left child node, and the return value is recorded as left;
  • Turn on the recursive right child node, and the return value is marked as right;

(3) Return value: it can be expanded into four cases according to left and right;

  1. When left and right are both empty: it means that the left / right subtree of root does not contain P and Q, and null is returned;
  2. When left and right are not empty at the same time: it means that P and Q are listed on the opposite side of root (in the left / right subtree respectively), so root is the nearest common ancestor and returns root;
  3. When left is empty, right is not empty: P and Q are not in the left subtree of root, and right is returned directly. There are two situations:
    • p. Q one of them is in the right subtree of root, and right points to P (assuming P);
    • p. Q both nodes are in the right subtree of root, and right at this time points to the nearest common ancestor node;
  4. When left is not empty and right is empty: P and Q are not in the right subtree of root, return to left directly, which is the same as case 3;
  5. It is observed that situation 1. Can be combined into 3. And 4. See code for details.

code:

4 code

Implementation of binary search tree algorithm:

public class LowestCommonAncestor {
    public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || p == null || q == null) {
            return null;
        }
        if (p.val > q.val)
            return lowestCommonAncestorCore(root, q, p);
        else
            return lowestCommonAncestorCore(root, p, q);
    }

    public static TreeNode lowestCommonAncestorCore(TreeNode root, TreeNode p, TreeNode q) {
        if (root.val >= p.val && root.val <= q.val) {
            return root;
        } else if (root.val > p.val && root.val > q.val) {
            return lowestCommonAncestorCore(root.left, p, q);
        } else {
            return lowestCommonAncestorCore(root.right, p, q);
        }
    }
}

Implementation of binary tree algorithm:

public class LowestCommonAncestor2 {
    public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;
        // Recursively traverse the left subtree. As long as p or q is found in the left subtree, whoever is found first will be returned
        TreeNode left = lowestCommonAncestor(root.left, p, q); 
        
        // Recursively traverse the right subtree. As long as p or q is found in the left subtree, whoever is found first will be returned
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left == null && right == null) return null; // 1.
        
        // If neither p nor q can be found in the left subtree, then both p and q must be in the right subtree. The closest common ancestor traversed in the right subtree is the nearest common ancestor (a node can also be its own ancestor)
        if (left == null) return right; // 3.
        
        // If left is not empty, a node (p or q) is found in the left subtree. At this time, judge the situation in the right subtree. If both p and Q cannot be found in the right subtree, then both p and Q must be in the left subtree. The first traversed in the left subtree is the nearest common ancestor (a node can also be its own ancestor)
        if (right == null) return left; // 4.
        
        // Otherwise, when both left and right are not empty, it means that the p and q nodes are on the opposite side of root, and the nearest common ancestor is root
        return root; // 2. if(left != null and right != null)
    }
}

reference resources
When solving the examples in this book, we refer to the solutions of some big guys, such as the official, K God and other blogs on leetcode. After the detailed explanation of each example, we will give the reference ideas or code links. Students can click in and have a look!

Reference to this example:
https://www.jianshu.com/p/edffe43abc14

If there are any deficiencies or mistakes in this article, you are welcome to criticize and correct. Finally, I hope to communicate with you and make progress and get your favorite offer!!!

Posted by ccb on Fri, 01 Oct 2021 18:26:44 -0700