A | '-- B | '-- C | | | '-- X1 | '-- D | '-- E | | | '-- F | | | '-- X2 | '-- X3
给定一棵树和该树中节点的多条路径,如何设计一种算法,该算法返回树内节点与其他节点的最小公共路径。
例如:
我们有以下x路径:
A -> B -> C -> x1
A -> B -> D -> E -> F -> x2
A -> B -> D -> x3
我们可以看到x2
和x3
共享一条共同路径:A -> B -> D
比x1
和x2
共享的路径长A -> B
x1
1}})或x3
和A -> B
(x1
)共享的路径。
我的目标是通过算法查找{{1}}。
答案 0 :(得分:2)
观察:从根向下到每个叶子,公共路径将从根到其最低共同的祖先节点。在此示例中,最低共同祖先是节点B和节点D.因此,此问题的结果将始终位于从根到最接近的最低共同祖先的路径中。
另请注意,从一个最低共同祖先节点向下一个级别,我们将始终拥有一个不包含任何其他最低共同祖先的路径。
所以我将提出一个解决方案,它需要在树中进行两次传递。
对于树中的每个节点,我们记录了一个附加信息,整数count
,它将告诉该节点,它包含叶子中有多少X对象(示例中为X1,X2,X3) )。
class Node {
int count = 0;
Node []children;
}
因此,通过一次通过,我们可以更新所有count
信息。
public int getCount(Node node){
if(children is empty){
if(this node contains object in the list)
return 1;
return 0;
}else{
for(Node child in children){
node.count += getCount(child);
}
}
return node.count;
}
在第二遍中,从根到每个叶子,问题的结果是在具有count == 1
并且最接近根的节点中。 nearest to the root
实际上是树中节点的级别。
因此,在示例中,我们对每个节点的计数是:
A:3(等级0)
B:3(1级)
C:1(级别2)包含结果,最接近根
D:2(第2级)
E:1(3级)
F:1(等级4)
因此,最后,时间复杂度为O(N),N为节点数。
注意:正如NiklasB提到的那样,整个过程可以一次完成。
只需修改getCount
方法以包含到根的距离,以及两个全局变量,min
保持计数的节点的最小距离为1,另一个保持对象结果。
int min = 100000;
Object result;
public int getCount(Node node, int distance){
if(children is empty){
if(this node contains object in the list)
if(distance < min){
min = distance;
result = //X
}
return 1;
return 0;
}else{
for(Node child in children){
node.count += getCount(child , distance + 1);
}
}
if(node.count == 1 && distance < min){
min = distance,
result = //X
}
return node.count;
}
答案 1 :(得分:1)
我们可以观察到共同路径的长度是第一个共同祖先的深度。因此,我们可以从确定相关节点的深度开始。
我们为表单的每个节点引入标签:
grandchildren - a subset of X of direct or indirect children of the node that are relevant
depth - the node's depth
我们为每个x引入了额外的标签:
commonAncestorDepth - the depth of the nearest common ancestor
所以我们从一些x开始:
mark the root with grandchildren = {}, depth = 0
for each x in X
n := x
inverseDepth = -1
//pass from node to first marked node
while n is not marked
n.depth = inverseDepth
inverseDepth -= 1
add x to n.grandchildren
n = n.parent
end while
//second pass to update depths
xDepth = n.depth - inverseDepth
while n.depth < 0
n.depth = xDepth + inverseDepth
n = n.parent
end while
x.commonAncestorDepth = n.Depth
//update the common ancestor of paths we might have crossed
for each n' in n.grandchildren
if n'.commonAncestorDepth < n.Depth
n'.commonAncestorDepth = n.depth
next
next
现在我们只需要在X中找到最小commonAncestorDepth
的元素。您可以将它们组织在优先级队列或类似结构中以快速获得此结果。
如果你有一棵大树并且X中只有很少的元素,那么这个算法是有效的。最坏情况下的复杂度是 O(树深* | X |),对于一棵完美的树,这是O(log N * | X |)其中N是节点数。