LeetCode 235 - Lowest Common Ancestor of a Binary Search Tree (BST)


Related: Lowest Common Ancestor in a Binary Tree
Given a binary search tree (BST), find the lowest common ancestor of two given nodes in the BST.
  1. Both nodes are to the left of the tree.
  2. Both nodes are to the right of the tree.
  3. One node is on the left while the other is on the right
  4. When the current node equals to either of the two nodes, this node must be the LCA too.
For case 1), the LCA must be in its left subtree. Similar with case 2), LCA must be in its right subtree. For case 3), the current node must be the LCA.
Therefore, using a top-down approach, we traverse to the left/right subtree depends on the case of 1) or 2), until we reach case 3), which we concludes that we have found the LCA.
A careful reader might notice that we forgot to handle one extra case. What if the node itself is equal to one of the two nodes? This is the exact case from our earlier example! (The LCA of 2 and 4 should be 2). Therefore, we add case number 4)
https://leetcode.com/discuss/66900/11ms-java-solution-3-lines
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root == null || p == null || q == null) return null; if(root.val<Math.min(p.val,q.val)) return lowestCommonAncestor(root.right,p,q); if(root.val>Math.max(p.val,q.val)) return lowestCommonAncestor(root.left,p,q); return root; }
http://www.fusu.us/2013/06/p3-lowest-common-ancestor-in-binary.html
public static Node lowestCommonAncestor(Node root, Node a, Node b) {
if (root == null || a == null || b == null) {
return null;
}
if (Math.max(a.data, b.data) < root.data) {
// both nodes are on the left
return lowestCommonAncestor(root.left, a, b);
} else
if (Math.min(a.data, b.data) > root.data) {
// both nodes are on the right
return lowestCommonAncestor(root.right, a, b);
} else {
// the nodes are on separate branches
return root;
}
}
Iterative Version:

https://leetcode.com/discuss/44946/my-java-solution
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 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; } }

http://blog.csdn.net/v_july_v/article/details/18312089
http://www.geeksforgeeks.org/lowest-common-ancestor-in-a-binary-search-tree/
May require check whether both n1 and n2 are found, - continue to search until both are found when find potential lca.
  1. public int query(Node t, Node u, Node v) {    
  2.     int left = u.value;    
  3.     int right = v.value;    
  4.     Node parent = null;    
  5.   
  6.     //二叉查找树内,如果左结点大于右结点,不对,交换  
  7.     if (left > right) {    
  8.         int temp = left;    
  9.         left = right;    
  10.         right = temp;    
  11.     }    
  12.   
  13.     while (true) {    
  14.         //如果t小于u、v,往t的右子树中查找  
  15.         if (t.value < left) {    
  16.             parent = t;    
  17.             t = t.right;    
  18.   
  19.         //如果t大于u、v,往t的左子树中查找  
  20.         } else if (t.value > right) {    
  21.             parent = t;    
  22.             t = t.left;    
  23.         } else if (t.value == left || t.value == right) {    
  24.             return parent.value;    
  25.         } else {    
  26.             return t.value;    
  27.         }    
  28.     }    
  29. }    
    static Node root;
     
    /* Function to find LCA of n1 and n2. The function assumes that both
     n1 and n2 are present in BST */
    Node lca(Node node, int n1, int n2) {
        if (node == null) {
            return null;
        }
        // If both n1 and n2 are smaller than root, then LCA lies in left
        if (node.data > n1 && node.data > n2) {
            return lca(node.left, n1, n2);
        }
        // If both n1 and n2 are greater than root, then LCA lies in right
        if (node.data < n1 && node.data < n2) {
            return lca(node.right, n1, n2);
        }
        return node;
    }

https://leetcode.com/discuss/44959/3-lines-with-o-1-space-1-liners-alternatives
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    while ((root.val - p.val) * (root.val - q.val) > 0) // not good approach, 
        root = p.val < root.val ? root.left : root.right;
    return root;
}
(in case of overflow, I'd do (root.val - (long)p.val) * (root.val - (long)q.val))
https://leetcode.com/discuss/44953/no-comparison-needed-java
O(n)
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; }

https://leetcode.com/articles/lowest-common-ancestor-of-a-binary-search-tree/
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // Value of p
        int pVal = p.val;
        // Value of q;
        int qVal = q.val;
        // Start from the root node of the tree
        TreeNode node = root;
        // Traverse the tree
        while (node != null) {

            // Value of ancestor/parent node.
            int parentVal = node.val;

            if (pVal > parentVal && qVal > parentVal) {
                // If both p and q are greater than parent
                node = node.right;
            } else if (pVal < parentVal && qVal < parentVal) {
                // If both p and q are lesser than parent
                node = node.left;
            } else {
                // We have found the split point, i.e. the LCA node.
                return node;
            }
        }
        return null;

    }
https://www.hikyle.me/archives/957/

思路 3:路径法(普通二叉树)

理论上,如果知道了 root 节点分别到节点 p、节点 q 的路径,就可以通过对比路径的重合部分,而 LCA 为重合部分的最后一项。
比如,之前给出的二叉树中,从根节点 6 到 0 的路径为 [6, 2, 0],从根节点 6 到 5 的路径为 [6, 2, 4, 5],则重合部分为 [6, 2],那么可知节点 2 为节点 0 和 节点 5 的 LCA。
    def lowestCommonAncestor(self, root, p, q):
        pathP, pathQ = self.getPath(root, p), self.getPath(root, q)
        lastSameNode = None
        for i in range(0, min(len(pathP), len(pathQ))):
            if pathP[i] == pathQ[i]:
                lastSameNode = pathP[i]
            else:
                break
        return lastSameNode
    # Get path from root => target (recursive)
    def getPath(self, root, target):
        path = []
     
        if root is None:
            return
     
        if root == target:
            return [root]
     
        left_subtree_path = self.getPath(root.left, target)
        right_subtree_path = self.getPath(root.right, target)
     
        if left_subtree_path:
            path.append(root)
            path.extend(left_subtree_path)
        elif right_subtree_path:
            path.append(root)
            path.extend(right_subtree_path)
        else:
            return
     
        return path

如果把 getPath 函数中的后序遍历递归算法改为非递归版,可以避免遇到一个很深的树导致递归层次超出规定范围(栈溢出,即真正的 Stack overflow)的问题。用栈 + 循环来模拟递归,是很常见的思路。
另外这个代码是为普通二叉树来写的。对于二叉查找树而言,可以更简单地找出路径

路径法(针对 BST 优化)

同样地,二叉查找树天生就是为了查找而生,所以查找时针对它的特点优化,可以非常快速地找到目标路径,无需回溯。
class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        pathP, pathQ = self.getPath(root, p), self.getPath(root, q)
        lastSameNode = None
        for i in range(0, min(len(pathP), len(pathQ))):
            if pathP[i] == pathQ[i]:
                lastSameNode = pathP[i]
            else:
                break
        return lastSameNode
    # Get path from root => target (recursive)
    # Only for a Binary Search Tree
    def getPath(self, root, target):
        path = [root]
        if target.val > root.val and root.right:
            path.extend(self.getPath(root.right, target))
        elif target.val < root.val and root.left:
            path.extend(self.getPath(root.left, target))
        elif target.val != root.val:        # If it reaches a leaf but still cannot find the target
            return
        else:
            pass     # Nothing to do. Just for completeness. You can delete the "else" branch anyway
        return path
Read full article from Lowest Common Ancestor of a Binary Search Tree (BST) | LeetCode

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts