转自: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. sql management studio正则替换sql

    需要把create proc xxx替换为 drop proc xxx go create proc xxx 方法,使用正则查找替换 create procedure {\[dbo\]\.[^\(]+ ...

  2. LeetCode 20 Valid Parentheses (括号匹配问题)

    题目链接 https://leetcode.com/problems/valid-parentheses/?tab=Description   Problem: 括号匹配问题. 使用栈,先进后出!   ...

  3. OpenStack Networking – FlatManager and FlatDHCPManager

    最好的分析FlatDHCPManager的源文,有机会把这篇翻译了 =========================== Over time, networking in OpenStack has ...

  4. 【转】失效迭代器(Invalidating Iterators)

      当一个容器变化时,指向该容器中元素的迭代器可能失效.这使得在迭代器变化期间改变容器容易出现问题.在这方面,不同的容器提供不同的保障:vectors: 引起内存重新分配的插入运算使所有迭代器失效,插 ...

  5. 【读书笔记】setsockopt

    setsockopt   设置套接口的选项. #include <sys/types.h> #include <sys/socket.h> int setsockopt(int ...

  6. .net C#中页面之间传值传参的六种方法

    1.QueryString是一种非常简单的传值方式,他可以将传送的值显示在浏览器的地址栏中.如果是传递一个或多个安全性要求不高或是结构简单的数值时,可以使用这个方法.但是对于传递数组或对象的话,就不能 ...

  7. Ubuntu 16.04系统下apt-get和dpkg区别

    两者的区别是dpkg绕过apt包管理数据库对软件包进行操作,所以你用dpkg安装过的软件包用apt可以再安装一遍,系统不知道之前安装过了,将会覆盖之前dpkg的安装.1.dpkg是用来安装.deb文件 ...

  8. VSCode集成TypeScript编译

    先安装github客户端和nodeJS客户端吧,直接去官网下载,nodeJS客户端安装完就集成了npm; 查看是否成功: git version  node -v npm-v 安装TypeScript ...

  9. 9.11 Django关于母版语言的灵活用法

    2018-9-11 12:45:38 还是这个连接 : http://www.cnblogs.com/liwenzhou/p/7931828.html 注意: 这节讲的是  母版语言的灵活用法!  可 ...

  10. POJ-1644 To Bet or Not To Bet(概率DP)

    To Bet or Not To Bet Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 1668 Accepted: 541 D ...