树:查找其公共路径相对于其他节点最短的节点

时间:2014-06-03 08:58:39

标签: algorithm

A
|
'-- B
    |
    '-- C
    |   |
    |   '-- X1
    |
    '-- D
        |
        '-- E
        |   |
        |   '-- F
        |       |
        |       '-- X2
        |
        '-- X3

给定一棵树和该树中节点的多条路径,如何设计一种算法,该算法返回树内节点与其他节点的最小公共路径。

例如:

我们有以下x路径:

  1. A -> B -> C -> x1
  2. A -> B -> D -> E -> F -> x2
  3. A -> B -> D -> x3
  4. 我们可以看到x2x3共享一条共同路径:A -> B -> Dx1x2共享的路径长A -> B x1 1}})或x3A -> Bx1)共享的路径。

    我的目标是通过算法查找{{1}}。

2 个答案:

答案 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是节点数。