转自: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 :

 public static Node lowestCommonAncestor(Node root, Node a, Node b) {
if (root == null) {
return null;
} if (root.equals(a) || root.equals(b)) {
// if at least one matched, no need to continue
// this is the LCA for this root
return root;
} Node l = lowestCommonAncestor(root.left, a, b);
Node r = lowestCommonAncestor(root.right, a, b); if (l != null && r != null) {
return root; // nodes are each on a seaparate branch
} // either one node is on one branch,
// or none was found in any of the branches
return l != null ? l : r;
}

For the node used we will use the following class:

 public class Node {
public int data;
public Node right;
public Node left; public Node(int data) {
this.data = data;
}
}

这个问题再follow up一下,就是要找到shortest path in a binary tree between two nodes

 public class Solution {
public static List<Node> shortestPath(Node root, Node a, Node b) {
ArrayList<Node> path1 = new ArrayList<Node>();
ArrayList<Node> path2 = new ArrayList<Node>();
Node LCA = lowestCommonAncestor(root, a, b);
helper(LCA.left, a, b, path1, new ArrayList<Node>());
helper(LCA.right, a, b, path2, new ArrayList<Node>());
Collections.reverse(path1);
path1.add(LCA);
path1.addAll(new ArrayList<Node>(path2));
return path1;
} public void helper(Node root, Node a, Node b, ArrayList<Node> outpath, ArrayList<Node> temp) {
if (root == null) return;
temp.add(root);
if (root == a || root == b) {
outpath = new ArrayList<Node>(temp);
return;
}
helper(root.left, a, b, outpath, temp);
helper(root.right, a, b, outpath, temp);
temp.remove(temp.size()-1);
}
}

别人的Stack做法,未深究 他说First stack is not really needed, a simple list would do - I just like symmetry.

 public static <V> void shortestpath(
Node<V> root, Node<V> a, Node<V> b,
Stack<Node<V>> outputPath) {
if (root == null) {
return;
}
if (root.data.equals(a.data) || root.data.equals(b.data)) {
outputPath.push(root);
return;
} shortestpath(root.left, a, b, outputPath);
shortestpath(root.right, a, b, outputPath); outputPath.push(root);
} public static List<Node> shortestPath(Node root, Node a, Node b) {
Stack<Node> path1 = new Stack<>();
Stack<Node> path2 = new Stack<>(); Node lca = lowestCommonAncestor(root, a, b); // This is to handle the case where one of the nodes IS the LCA
Node r = lca.equals(a) ? a : (lca.equals(b) ? b : lca); shortestpath(r.left, a, b, path1);
shortestpath(r.right, a, b, path2); path1.push(r);
// invert the second path
while (!path2.isEmpty()) {
path1.push(path2.pop());
}
return path1;
}

Summary: Lowest Common Ancestor in a Binary Tree & Shortest Path In a Binary Tree的更多相关文章

  1. Range Minimum Query and Lowest Common Ancestor

    作者:danielp 出处:http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAnc ...

  2. A1143. Lowest Common Ancestor

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  3. PAT A1143 Lowest Common Ancestor (30 分)——二叉搜索树,lca

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  4. 1143 Lowest Common Ancestor

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  5. PAT 甲级 1143 Lowest Common Ancestor

    https://pintia.cn/problem-sets/994805342720868352/problems/994805343727501312 The lowest common ance ...

  6. PAT 1143 Lowest Common Ancestor[难][BST性质]

    1143 Lowest Common Ancestor(30 分) The lowest common ancestor (LCA) of two nodes U and V in a tree is ...

  7. [PAT] 1143 Lowest Common Ancestor(30 分)

    1143 Lowest Common Ancestor(30 分)The lowest common ancestor (LCA) of two nodes U and V in a tree is ...

  8. 1143. Lowest Common Ancestor (30)

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  9. PAT 1143 Lowest Common Ancestor

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

随机推荐

  1. Oracle 学习之触发器

    1. 触发器简介 触发器是存储在数据库服务器中的程序单元,当一个表或一个视图被改变,或者数据库发生某些事件时,Oracle会自动触发触发器,并执行触发器中的代码.只有在触发器中定义的事件发生时,触发器 ...

  2. 题目1005:Graduate Admission(录取算法)

    题目链接:http://ac.jobdu.com/problem.php?pid=1005 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...

  3. 查看JVM使用的默认的垃圾收集器

    一.查看步骤 cmd执行命令: java -XX:+PrintCommandLineFlags -version 输出如下(举例): 针对上述的-XX:UseParallelGC,这边我们引用< ...

  4. js json转字符串

    在数据传输过程中,json是以文本,即字符串的形式传递的,而JS操作的是JSON对象,所以,JSON对象和JSON字符串之间的相互转换是关键.例如:JSON字符串:var str1 = '{ &quo ...

  5. cocos2d-x学习之旅(五):1.5 使用eclipse编译cocos2d-x示例项目,创建cocos2d-x android项目并部署到真机

    今天将cocos2d-x的示例项目tests编译到android真机运行,以及如何创建cocos2d-x的android项目. 打开cocos2d-x的tests项目,路径为:D:\cocos2d-x ...

  6. kafka 的quick start(windows平台)

    h1, h2, h3, h4, h5, h6, p, blockquote { margin: 5px; padding: 5; } body { font-family: "Helveti ...

  7. C语言qsort用法

    一.对int类型数组排序 int num[100]; Sample: int cmp ( const void *a , const void *b ) { return *(int *)a - *( ...

  8. 23种设计模式之备忘录模式(Memento)

    备忘录模式确保在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态.备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定 ...

  9. 【BZOJ4036】[HAOI2015]按位或 FWT

    [BZOJ4036][HAOI2015]按位或 Description 刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal的or ...

  10. 初始react

    很久就期待学习react了,惰性,一直都没有去翻阅react的资料,最近抽空,简单的了解了一下react,先记录一下,后续慢慢的学习. 一.ReactJS简介 React 起源于 Facebook 的 ...