Cracking the Coding Interview, 4 Edition - 150 Programming Interview Questions and Solutions
Cracking the Coding Interview, 4 Edition - 150 Programming Interview Questions and Solutions
Cracking the Coding Interview, 4 Edition - 150 Programming Interview Questions and Solutions
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Solutions</strong> to Chapter 4 | Trees <strong>and</strong> Graphs<br />
4.6 Design an algorithm <strong>and</strong> write code to find <strong>the</strong> first common ancestor of two nodes<br />
in a binary tree. Avoid storing additional nodes in a data structure. NOTE: This is not<br />
necessarily a binary search tree.<br />
SOLUTION<br />
pg 54<br />
If this were a binary search tree, we could do a modified find on <strong>the</strong> two nodes <strong>and</strong> see where<br />
<strong>the</strong> paths diverge. Unfortunately, this is not a binary search tree, so we much try o<strong>the</strong>r approaches.<br />
Attempt #1:<br />
If each node has a link to its parent, we could trace p <strong>and</strong> q’s paths up until <strong>the</strong>y intersect.<br />
Attempt #2:<br />
Alternatively, you could follow a chain in which p <strong>and</strong> q are on <strong>the</strong> same side. That is, if p <strong>and</strong><br />
q are both on <strong>the</strong> left of <strong>the</strong> node, branch left to look for <strong>the</strong> common ancestor. When p <strong>and</strong><br />
q are no longer on <strong>the</strong> same side, you must have found <strong>the</strong> first common ancestor.<br />
1 public Tree commonAncestor(Tree root, Tree p, Tree q) {<br />
2 if (covers(root.left, p) && covers(root.left, q))<br />
3 return commonAncestor(root.left, p, q);<br />
4 if (covers(root.right, p) && covers(root.right, q))<br />
5 return commonAncestor(root.right, p, q);<br />
6 return root;<br />
7 }<br />
8 private boolean covers(Tree root, Tree p) { /* is p a child of root? */<br />
9 if (root == null) return false;<br />
10 if (root == p) return true;<br />
11 return covers(root.left, p) || covers(root.right, p);<br />
12 }<br />
What is <strong>the</strong> running time of this algorithm? One way of looking at this is to see how many<br />
times each node is touched. Covers touches every child node, so we know that every single<br />
node in <strong>the</strong> tree must be touched at least once, <strong>and</strong> many nodes are touched multiple times.<br />
Attempt #3:<br />
For any node r, we know <strong>the</strong> following:<br />
1. If p is on one side <strong>and</strong> q is on <strong>the</strong> o<strong>the</strong>r, r is <strong>the</strong> first common ancestor.<br />
2. Else, <strong>the</strong> first common ancestor is on <strong>the</strong> left or <strong>the</strong> right side.<br />
So, we can create a simple recursive algorithm called search that calls search(left side) <strong>and</strong><br />
search(right side) looking at how many nodes (p or q) are placed from <strong>the</strong> left side <strong>and</strong> from<br />
<strong>the</strong> right side of <strong>the</strong> current node. If <strong>the</strong>re are two nodes on one of <strong>the</strong> sides, <strong>the</strong>n we have<br />
CareerCup.com<br />
1 2 8