我尝试使用Tarjan的算法和网站上的一种算法来解决问题:http://discuss.techinterview.org/default.asp?interview.11.532716.6,但没有一个是清楚的。也许我的递归概念没有正确构建。请举小举例解释以上两个例子。我对Union Find数据结构有所了解。
看起来非常有趣的问题。所以必须解决问题无论如何。准备面试。
如果存在任何其他逻辑/算法,请分享。
答案 0 :(得分:4)
LCA算法尝试做一件简单的事情:找出从两个节点到根节点的路径。现在,这两个路径将具有共同的后缀(假设路径在根处结束)。 LCA是后缀开始的第一个节点。
考虑以下树:
r * / \ s * * / \ u * * t / / \ * v * * / \ * *
为了找到LCA(u,v),我们按如下方式进行:
现在,我们检查公共后缀:
请注意,实际算法 不 一直到根。他们使用Disjoint-Set数据结构在到达s时停止。
解释了一套优秀的替代方法here。
答案 1 :(得分:1)
由于你提到了求职面试,我想到了这个问题的变化,你只能使用O(1)内存。
在这种情况下,请考虑以下算法:
1)从节点u扫描树到根,找到路径长度L(u)
2)从节点v扫描树到根,找到路径长度L(v)
3)计算路径长度差D = | L(u)-L(v)|
4)从根
中删除较长路径中的D节点5)从两个节点并行向上走树,直到你到达同一个节点
6)将此节点作为LCA返回
答案 2 :(得分:-1)
假设您只需要解决一次问题(每个数据集),那么一个简单的方法是从一个节点(连同自身)收集祖先集,然后从另一个节点中走出祖先列表,直到找到上述集合中的成员,必然是最低共同祖先。给出了伪代码:
Let A and B begin as the nodes in question.
seen := set containing the root node
while A is not root:
add A to seen
A := A's parent
while B is not in seen:
B := B's parent
B is now the lowest common ancestor.
另一种方法是为每个节点计算整个路径到房间,然后从右侧扫描以查找公共后缀。它的第一个要素是LCA。其中哪一项更快取决于您的数据。
如果您需要找到多对节点的LCA,那么您可以进行各种空间/时间权衡:
例如,你可以预先计算每个节点的深度,这样你就可以避免每次重新创建集合(或路径),首先从较深的节点走到较浅节点的深度,然后在锁定步骤中将两个节点移向根节点:当这些路径相遇时,您就拥有了LCA。另一种方法使用深度模型H的下一个祖先来注释节点,这样您首先要解决类似但小时H的问题,然后解决第一个问题的H大小的实例。这在非常深的树上很好,通常选择H作为树的平均深度的平方根。