Summary: Lowest Common Ancestor in a Binary Tree & Shortest Path In a Binary Tree
转自:Pavel's Blog
Now let's say we want to find the LCA for nodes 4 and 9, we will need to traverse the whole tree to compare each node so that we can locate the nodes. Now considering that we start the traversal with the left branch (pre-order traversal) - we have the worst case here with O(n) running time.
Traversing the tree we compare the current node with both of the nodes and if one of them match, it means that one is the LCA on the respective branch. Let's say after traversing the above tree in pre-order the first node that matches our nodes is 9 (2, 7, 2, 6, 5, 11, 5, 9). So the first obvious thought is that the 4 must be a child of 9, since we're already on the right child of node 5 and the pre-order traversal looks at the node first, then the left child and lastly the right child. Then we note node 9 as the LCA and we don't have to look further anymore.
Let's use another case, say we're looking for the LCA of 7 and 9. The first node in our pre-order traversal (2, 7, 2, 6, 5, 11, 5, 9, 4) is 7. Now here we can say that the LCA for the left branch is 7 because again, if the second node is in the same branch, independently of where and how deep it will be in this branch, the LCA will still be 7; thus we don't have to look in this branch anymore. But we still did not look at the right branch, so we keep traversing in a pre-order manner, but now omitting the other nodes: 2, 7, 5, 9. Now we can say that the LCA for that branch is 9. We can also affirm that the LCA for the branch with the root in node 5 is also 9. And in the end we have our nodes both in separate branches, which means that the LCA is the root of those branches - node 2.
The algorithm looks as a modified version of a pre-order tree traversal :
1 public static Node lowestCommonAncestor(Node root, Node a, Node b) { 2 if (root == null) { 3 return null; 4 } 5 6 if (root.equals(a) || root.equals(b)) { 7 // if at least one matched, no need to continue 8 // this is the LCA for this root 9 return root; 10 } 11 12 Node l = lowestCommonAncestor(root.left, a, b); 13 Node r = lowestCommonAncestor(root.right, a, b); 14 15 if (l != null && r != null) { 16 return root; // nodes are each on a seaparate branch 17 } 18 19 // either one node is on one branch, 20 // or none was found in any of the branches 21 return l != null ? l : r; 22 }
For the node used we will use the following class:
1 public class Node { 2 public int data; 3 public Node right; 4 public Node left; 5 6 public Node(int data) { 7 this.data = data; 8 } 9 }
这个问题再follow up一下,就是要找到shortest path in a binary tree between two nodes
1 public class Solution { 2 public static List<Node> shortestPath(Node root, Node a, Node b) { 3 ArrayList<Node> path1 = new ArrayList<Node>(); 4 ArrayList<Node> path2 = new ArrayList<Node>(); 5 Node LCA = lowestCommonAncestor(root, a, b); 6 helper(LCA.left, a, b, path1, new ArrayList<Node>()); 7 helper(LCA.right, a, b, path2, new ArrayList<Node>()); 8 Collections.reverse(path1); 9 path1.add(LCA); 10 path1.addAll(new ArrayList<Node>(path2)); 11 return path1; 12 } 13 14 public void helper(Node root, Node a, Node b, ArrayList<Node> outpath, ArrayList<Node> temp) { 15 if (root == null) return; 16 temp.add(root); 17 if (root == a || root == b) { 18 outpath = new ArrayList<Node>(temp); 19 return; 20 } 21 helper(root.left, a, b, outpath, temp); 22 helper(root.right, a, b, outpath, temp); 23 temp.remove(temp.size()-1); 24 } 25 }
别人的Stack做法,未深究 他说First stack is not really needed, a simple list would do - I just like symmetry.
1 public static <V> void shortestpath( 2 Node<V> root, Node<V> a, Node<V> b, 3 Stack<Node<V>> outputPath) { 4 if (root == null) { 5 return; 6 } 7 if (root.data.equals(a.data) || root.data.equals(b.data)) { 8 outputPath.push(root); 9 return; 10 } 11 12 shortestpath(root.left, a, b, outputPath); 13 shortestpath(root.right, a, b, outputPath); 14 15 outputPath.push(root); 16 } 17 18 public static List<Node> shortestPath(Node root, Node a, Node b) { 19 Stack<Node> path1 = new Stack<>(); 20 Stack<Node> path2 = new Stack<>(); 21 22 Node lca = lowestCommonAncestor(root, a, b); 23 24 // This is to handle the case where one of the nodes IS the LCA 25 Node r = lca.equals(a) ? a : (lca.equals(b) ? b : lca); 26 27 shortestpath(r.left, a, b, path1); 28 shortestpath(r.right, a, b, path2); 29 30 path1.push(r); 31 // invert the second path 32 while (!path2.isEmpty()) { 33 path1.push(path2.pop()); 34 } 35 return path1; 36 }