package com.freetymekiyan.algorithms.level.medium;
import com.freetymekiyan.algorithms.utils.Utils;
import com.freetymekiyan.algorithms.utils.Utils.TreeNode;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
* <p>
* According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as
* the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”
* <p>
* | _______3______
* | / \
* | ___5__ ___1__
* | / \ / \
* | 6 _2 0 8
* | / \
* | 7 4
* For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. Another example is LCA of nodes 5 and 4 is 5,
* since a node can be a descendant of itself according to the LCA definition.
* <p>
* Company Tags: Amazon, LinkedIn, Apple, Facebook, Microsoft
* Tags: Tree
* Similar Problems: (E) Lowest Common Ancestor of a Binary Search Tree
*/
public class LowestCommonAncestorOfABinaryTree {
private LowestCommonAncestorOfABinaryTree l;
/**
* Recursive.
* Recurrence relation:
* Search for p and q in left and right subtrees.
* If both are found, it means the two nodes are in different subtrees, root should be their LCA.
* If one of them is null, it means no possible LCA found for p or q.
* Then the one that is not null should be their LCA.
* Base case:
* If root is null, return null; if root is p or q, return p or q, respectively.
*/
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);
return left == null ? right : right == null ? left : root;
}
/**
* Iterative. Two stacks.
* First, DFS for p or q.
* If we found anyone of them, copy current stack to a new stack, which keeps all its ancestors.
* Then we try to find the other one.
* Every time when we pop from the stack for DFS, we check whether the node is the found node's ancestor.
* If it is, pop the top node from ancestor and update lca to it.
* If it's not, do nothing.
* When we find the other node, return lca.
*/
public TreeNode lowestCommonAncestorB(TreeNode root, TreeNode p, TreeNode q) {
Deque<TreeNode> stack = new ArrayDeque<>();
Deque<TreeNode> ancestors = null;
TreeNode lca = null;
TreeNode next = null;
while (!stack.isEmpty() || root != null) {
if (root != null) {
stack.push(root);
root = root.left;
} else {
root = stack.pop();
// Visit
if (lca == null) {
if (root == p || root == q) {
ancestors = new ArrayDeque<>(stack);
lca = root;
next = lca == p ? q : p;
}
} else {
// Update lca when root is lca's ancestor
if (!ancestors.isEmpty() && ancestors.peek() == root) {
lca = ancestors.pop();
}
if (root == next) {
break;
}
}
root = root.right;
}
}
return lca;
}
@Before
public void setUp() {
l = new LowestCommonAncestorOfABinaryTree();
}
/**
* Iterative solution cannot pass this test the binary tree is built in Utils.
* There is no good way to get reference to the nodes inside.
* Only comparing the value is not enough.
*/
@Test
public void testExamples() {
TreeNode root = Utils.buildBinaryTree(new Integer[]{3, 5, 1, 6, 2, 0, 8, null, null, 7, 4});
TreeNode p = new TreeNode(5);
TreeNode q = new TreeNode(1);
TreeNode res = l.lowestCommonAncestor(root, p, q);
Assert.assertNotNull(res);
Assert.assertEquals(3, res.val);
root =
Utils.buildBinaryTree(
new Integer[]{37, -34, -48, null, -100, -100, 48, null, null, null, null, -54, null, -71, -22, null,
null, null, 8});
p = new TreeNode(-100);
q = new TreeNode(-71);
res = l.lowestCommonAncestor(root, p, q);
Assert.assertNotNull(res);
Assert.assertEquals(-48, res.val);
}
@After
public void tearDown() {
l = null;
}
}