[sword finger Offer] personal learning notes_ 68_ Nearest common ancestor of binary search tree

Posted by npsari on Tue, 08 Feb 2022 02:50:43 +0100

Brushing date: 7:49 am Sunday, May 30, 2021

Personal question brushing records and code collection are all from leetcode

After much discussion and consultation, we now intend to work in the direction of Java

The main answer language is Java

Title:

Sword finger Offer 68 - I. nearest common ancestor of binary search tree

Simple difficulty 131

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

Baidu Encyclopedia The definition of nearest common ancestor in is: "for two nodes p and q with root tree T, the nearest common ancestor is expressed as a node x, which satisfies 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 search tree: root = [6,2,8,0,4,7,9,null,null,3,5]

Example 1:

input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
 output: 6 
explain: The nearest common ancestor of node 2 and node 8 is 6.

Example 2:

input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
 output: 2
 explain: The nearest common ancestor of node 2 and node 4 is 2, Because according to the definition, the nearest common ancestor node can be the node itself.

explain:

  • The values of all nodes are unique.
  • p. q is different nodes and exists in a given binary search tree.

Topic analysis

The characteristic of binary search tree is that it is already orderly.

Binary tree problem is likely to involve recursion. In the search, if the left is less than the root and the right is greater than the root, then the root node will be returned directly. If both the left and right are greater than, then go to the right. If both the left and right are less than, then go to the left.

The question defines that the input must exist, so which node to find first and which node is the nearest public node.

Initial answer:

No white practice, no white practice, perfect ending, one-time pass

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    TreeNode res;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val > q.val) {
            TreeNode tmp = p;
            p = q;
            q = tmp;
        } //Make sure p is small and q is large
        if(root.val <= q.val && root.val >= p.val) return root;
        if(root.val < p.val) res = lowestCommonAncestor(root.right, p, q);
        if(root.val > p.val) res = lowestCommonAncestor(root.left, p, q);
        return res;
    }
}

Execution result: passed

Show details add notes

Execution time: 6 ms, beating 100.00% of users in all Java submissions

Memory consumption: 38.6 MB, beating 96.88% of users in all Java submissions

Learn from others:

Method 1:

Gem L2 (edited) February 14, 2020

Java problem solving is simple and clear

Make good use of the properties of binary search tree.

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        
        if (root == null)
            return null;
        
        if (root.val > p.val && root.val > q.val)
            return lowestCommonAncestor(root.left, p, q);
        if (root.val < p.val && root.val < q.val)
            return lowestCommonAncestor(root.right, p, q);

        return root;
    }

Method 2:

JoCai 2021-02-21

Java:

Execution time: 6 ms, beating 100.00% of users in all Java submissions

Memory consumption: 39.3 MB, beating 17.36% of users in all Java submissions

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null) return null;
        if(p==root || q==root) return root;
        //Both nodes are on the left of the current node
        if(p.val<root.val && q.val<root.val){
            return lowestCommonAncestor(root.left,p,q);//Find the left
        }
        //Both nodes are on the right of the current node
        else if(p.val>root.val && q.val>root.val){
            return lowestCommonAncestor(root.right,p,q);//Look to the right
        }
        //Or one left and one right
        else{
            return root;
        }
    }
}

Method 3:

mata Chuan L5

2020-03-13

You can write a few case s and draw a picture to see. It's easy to find the law

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //When p and q are smaller than root, according to the nature of binary search tree, the common ancestor of p and q must be on the left subtree of root
        if (p.val < root.val && q.val < root.val) {
            return lowestCommonAncestor(root.left, p, q);
        }
        //Similarly, when P and Q are larger than root, go to the right subtree of root.
        if (p.val > root.val && q.val > root.val) {
            return lowestCommonAncestor(root.right, p, q);
        }
        
        //If p or q is equal to root, it means that the node equal to is the common ancestor
        //Otherwise, p and q are on both sides of root, and root is the common ancestor
        if (p.val == root.val) {
            return p;
        } else if (q.val == root.val) {
            return q;
        } else {
            return root;
        }
    } 
}

Method 4:

Roderland 2020-08-06

Iterative version

class Solution {
   public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // if (p==root||q==root) return root;
        // if (p==q) return p;
        while (true) {
            if (root.val>p.val&&root.val>q.val) root=root.left;
            else if (root.val<p.val&&root.val<q.val) root=root.right;
            else return root;
        }
    }
}

Method 5

K God

Author: jyd
Link: https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/solution/mian-shi-ti-68-i-er-cha-sou-suo-shu-de-zui-jin-g-7/
Source: LeetCode

Method 1: iteration

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        while(root != null) {
            if(root.val < p.val && root.val < q.val) // p. Q is in the right subtree of root
                root = root.right; // Traverse to the right child node
            else if(root.val > p.val && root.val > q.val) // p. Q is in the left subtree of root
                root = root.left; // Traverse to the left child node
            else break;
        }
        return root;
    }
}

Optimization: if p.val < q.val can be guaranteed, the judgment conditions can be reduced in the cycle.

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val > q.val) { // Guarantee p.val < q.val
            TreeNode tmp = p;
            p = q;
            q = tmp;
        }
        while(root != null) {
            if(root.val < p.val) // p. Q is in the right subtree of root
                root = root.right; // Traverse to the right child node
            else if(root.val > q.val) // p. Q is in the left subtree of root
                root = root.left; // Traverse to the left child node
            else break;
        }
        return root;
    }
}

Method 2: recursion

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root.val < p.val && root.val < q.val)
            return lowestCommonAncestor(root.right, p, q);
        if(root.val > p.val && root.val > q.val)
            return lowestCommonAncestor(root.left, p, q);
        return root;
    }
}

Title:

Sword finger Offer 68 - II Nearest common ancestor of binary tree

Simple difficulty 258

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

Baidu Encyclopedia The definition of nearest common ancestor in is: "for two nodes p and q with root tree T, the nearest common ancestor is expressed as a node x, which satisfies 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, give the following binary tree: root = [3,5,1,6,2,0,8,null,null,7,4]

Example 1:

input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
 output: 3
 explain: 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
 explain: 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.

explain:

  • The values of all nodes are unique.
  • p. q is different nodes and exists in a given binary tree.

Topic analysis

Instead of searching a binary tree, you must find two nodes before it ends.

Goo Goo L1 (edited) 2021-05-04

The recursive understanding of this problem is actually not so simple. If the function is directly understood as finding the nearest ancestor node of pq in the tree with root as the root, it is not accurate. The definition of this recursive function should be extended. In fact, the functions of this function are:

  1. If there are pq two nodes in the tree with root, the nearest ancestor is returned
  2. If there is only one node in the tree with root as the root, the node containing is returned
  3. If neither node exists, nullptr is returned

After expanding the definition of the function, it conforms to the logic of the return value in the function body, and can just realize the required functions.

Initial answer:

Refer to the above analysis and solutions

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left == null) return right;
        if(right == null) return left;
        return root;
    }
}

Execution result: passed

Show details add notes

Execution time: 7 ms, beating 99.98% of users in all Java submissions

Memory consumption: 39.9 MB, beating 45.15% of users in all Java submissions

Learn from others:

Method 1:

...L1 (edited) February 13, 2020

class Solution {
    /**
     * Nearest common ancestor of binary tree
     * Idea:
     * Three situations:
     * 1,p q If one is in the left subtree and the other is in the right subtree, the current node is the nearest common ancestor
     * 2,p q All in the left subtree 
     * 3,p q All in the right subtree
     * @param root
     * @param p
     * @param q
     * @return
     */
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        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);
        if (left != null && right != null) {
            // p q one on the left and one on the right
            return root;
        }
        if (left != null) {
            // Both P and Q are in the left subtree
            return left;
        }
        if (right != null) {
            // Both P and Q are in the right subtree
            return right;
        }
        return null;
    }
}

Method 2:


Nanjun1998

(edited) March 17, 2021

The practice in the book of sword finger offer is not time efficient, but it is also easy to understand

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        Deque<TreeNode> stackP = new ArrayDeque<>();
        Deque<TreeNode> stackQ = new ArrayDeque<>();
        getTrace(root,p,stackP); // Get the path of p and q
        getTrace(root,q,stackQ);
        TreeNode ret = root;
        while(!stackP.isEmpty()&&!stackQ.isEmpty()){
            TreeNode nodeQ = stackQ.poll();
            TreeNode nodeP = stackP.poll();
            if(nodeP.val!=nodeQ.val) return ret;
            ret = nodeP;   //ret is the last same node
        }
        return ret;
    }

    private boolean getTrace(TreeNode root , TreeNode node, Deque<TreeNode> stack){       
        stack.addLast(root);
        boolean hasGet=false;
        if(root.val == node.val)  return true;
        if(root.left!=null)  hasGet = getTrace(root.left,node,stack);
        if(!hasGet && root.right!=null&&!stack.isEmpty()) hasGet = getTrace(root.right,node,stack);
        if(!hasGet&&!stack.isEmpty())  stack.removeLast();
        return hasGet;
    }
}

The follow-up traversal is time-efficient and the code is concise and beautiful, but it doesn't feel very good_ ( ¦ 3」∠)_

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null||root==p||root==q) return root;
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        if(left!=null && right!=null) return root;
        if(left!=null) return left;
        if(right!=null) return right;
        return null;
    }
}

Method 3:

Brise Three days ago

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        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);
        if(left ==null){
            return right; 
        }else if(right==null){
            return left;
        }else{
            return root;
        }
    }
}

Method 4:

Bad morisheng L1 2020-10-28

p and q are the nodes in the tree;

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null)
            return null;
        
        if(p == root || q == root)
            return root;
        
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);

        if(left != null && right != null)
            return root;
        return left == null ? right : left;
    }
}

Method 5

K God

Author: jyd
Link: https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/solution/mian-shi-ti-68-ii-er-cha-shu-de-zui-jin-gong-gon-7/
Source: LeetCode

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left == null) return right;
        if(right == null) return left;
        return root;
    }
}

Expanded writing

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left == null && right == null) return null; // 1.
        if(left == null) return right; // 3.
        if(right == null) return left; // 4.
        return root; // 2. if(left != null and right != null)
    }
}

summary

The above is the content and learning process of this topic. Basically, there is a cycle when there is recursion. There have been two months intermittently. Then there is review, and then brush the topic to maintain the feel. Come on, everyone.

Sprinkle flowers at the end~

Welcome discussion and common progress.

Topics: Java Algorithm data structure leetcode Binary tree