27.10.2014 Views

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

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!