I'm looking at two solutions to find the first common ancestor of two nodes in a binary tree (not necessarily a binary search tree). I've been told the second solution provides a better worst-case run time, but I can't figure out why. Can someone please enlighten me?
Solution 1:
- Find the depth of each of the two nodes: p,q
- Calculate the delta of their depths
- Set a pointer at the shallower node a pointer a the deeper node
- Move the deeper node pointer up by the delta so we can start traversing from the same height
- Recursively visit the part nodes of both pointers until we arrive at the same node which is out the first common ancestor
import com.foo.graphstrees.BinaryTreeNodeWithParent;
/*
Find the first common ancestor to 2 nodes in a binary tree.
*/
public class FirstCommonAncestorFinder {
public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent p, BinaryTreeNodeWithParent q) {
int delta = depth(p) - depth(q);
BinaryTreeNodeWithParent first = delta > 0 ? q: p; // use shallower node
BinaryTreeNodeWithParent second = delta > 0 ? p: q; //use deeper
second = goUp(second, delta); // move up so they are level, if 1 node is deeper in the tree than the other, their common ancestor obviously cannot be below the shallower node, so we start them off at the same height in the tree
//keep going up the tree, once first == second, stop
while(!first.equals(second) && first !=null && second !=null) {
first = first.getParent();
second = second.getParent();
}
return first == null || second == null ? null : first;
}
private int depth(BinaryTreeNodeWithParent n) {
int depth = 0;
while (n != null) {
n = n.getParent();
depth++;
}
return depth;
}
private BinaryTreeNodeWithParent goUp(BinaryTreeNodeWithParent node, int delta) {
while (delta > 0 && node != null) {
node = node.getParent();
delta--;
}
return node;
}
}
Solution 2:
- Verify both nodes (p,q) exist in the tree starting at the root node
- Verify that q is not a child of p and p is not a child of q by traversing their subtrees
- Recursively examine subtrees of successive parent nodes of p until q is found
import com.foo.graphstrees.BinaryTreeNodeWithParent;
public class FirstCommonAncestorImproved {
public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent root,
BinaryTreeNodeWithParent a,
BinaryTreeNodeWithParent b) {
if (!covers(root, a) || !covers(root, b)) {
return null;
} else if (covers(a, b)) {
return a;
} else if (covers(b, a)) {
return b;
}
var sibling = getSibling(a);
var parent = a.getParent();
while (!covers(sibling, b)) {
sibling = getSibling(parent);
parent = parent.getParent();
}
return parent;
}
private BinaryTreeNodeWithParent getSibling(BinaryTreeNodeWithParent node) {
if (node == null || node.getParent() == null) return null;
var parent = node.getParent();
return node.equals(parent.getLeft()) ? node.getRight() : node.getLeft();
}
private boolean covers(BinaryTreeNodeWithParent root,
BinaryTreeNodeWithParent node) {
if (root == null) return false;
if (root.equals(node)) return true;
return covers(root.getLeft(), node) || covers(root.getRight(), node);
}
}