【问题标题】:Algorithm - Finding the number of pairs with diameter distance in a tree?算法 - 查找树中直径距离的对数?
【发布时间】:2015-04-17 16:12:11
【问题描述】:

我有一个无根双向未加权非二叉树。我知道如何找到树的直径,即树中任何一对点之间的最大距离,但我有兴趣找到具有该最大距离的对数。有没有一种算法可以在 O(V^2) 时间内找到直径距离的对数,其中 V 是节点数?

谢谢!

【问题讨论】:

    标签: algorithm tree


    【解决方案1】:

    是的,有一个 O(V+E) 时间的算法。它只是查找直径的修改版本。

    正如我们所知,我们可以使用 BFS 的两次调用来找到直径,方法是首先在任何节点上进行第一次调用,然后记住发现的最后一个节点 u 并运行第二次调用 BFS(u),然后记住发现的最后一个节点,比如说v.u和v之间的距离就是直径。

    得出具有该最大距离的对数。

    1.在调用第一个BFS之前,初始化一个长度为|V|的数组距离distance[s]=0.s 是任何节点上第一次 BFS 调用的起始顶点。

    2.在BFS中,修改while循环为:

    while(Q is not empty)
     {
      e=deque(Q);
        for all vertices w adjacent to e
         {
           if(w is not visited)
            {
             enque(w)
             mark w as visited
             distance[w]=distance[e]+1
             parent[w]=e
            }
         }
     }
    

    3.就像我说的,记住最后访问的节点,说你是那个节点。现在计算与顶点 u 处于同一级别的顶点数。 mark 是一个长度为 n 的数组,它的所有值都初始化为 0,0 意味着最初没有计算顶点。

    n1=0
    for i = 1 to number of vertices
     {
      if(distance[i]==distance[u]&&mark[i]==0)
       {
        n1++
        mark[i]=1/*vertex counted*/
       }
     }  
    

    n1 给出了与顶点 u 处于同一级别的顶点的数量,现在对于所有具有 mark[i] = 1 的顶点,都被标记并且不会再次计数。

    4.类似地,在对 u 执行第二次 BFS 之前,初始化另一个长度为 |V| 的数组 distance2和距离2[u]=0。

    5.运行 BFS(u) 并再次获取发现的最后一个节点,比如 v

    6. 重​​复第 3 步,这一次在 distance2 数组上并采用不同的变量,例如 n2=0,条件为

    if(distance2[i]==distance2[v]&&mark[i]==0)
     n2++
    else if(distance2[i]==distance2[v]&&mark[i]==1)
     set_common=1
    

    7.set_common 是一个全局变量,当存在一组顶点时设置,使得任何两个顶点之间的路径是直径的路径,并且第一个 bfs 没有标记所有这些顶点,但确实标记了至少一个这就是为什么mark[i]==1。

    假设第一个 bfs 确实在第一次调用中标记了所有这样的顶点,那么 n2 将 = 0 并且 set_common 不会被设置,也不需要。但是这种情况与上面相同

    无论如何,给出直径的对数是:=

    (n+n2)组合2 - X=(n1+n2)!/((2!)((n1+n2-2)!)) - X

    我将详细说明 X 是什么。否则对数 = n1*n2,即 2 个不相交的顶点集给出直径时的情况

    所以使用的条件是

      if(n2==0||set_common==1)
        number_of_pairs=(n1+n2)C2-X
      else n1*n2
    

    现在谈论X。可能会出现被标记的顶点可能有共同的父节点。在这种情况下,我们不能计算那里的组合。所以在使用上述条件之前,建议运行以下算法

      X=0/*Initialize*/
     for(i = 1 to number of vertices)
      {
       s = 0,p = -1
       if(mark[i]==0)
        continue
       else
       {
        s++
        if(p==-1)
         p=parent[i]
        while((i+1)<=number_of_vertices&& p==parent[i+1])
         {s++;i++}
       }
       if(s>1)
        X=X+sC2
     }
    

    正确性证明

    这很容易。由于 BFS 逐级遍历树,n1 会给出 u 层的顶点数,n2 会给出 v 层的顶点数并且由于u和v之间的距离=直径。因此,u水平上的任何顶点与v水平上的任何顶点之间的距离将等于直径。

    所用时间为 2(|V|) + 2*time_of_DFS=O(V+E)。

    【讨论】:

    • 如果有一个父节点和该父节点的 V-1 个子节点,这仍然有效吗?
    • 我已经进行了必要的编辑并涵盖了您提到的案例。如果我错了,请纠正我。
    • 我认为你不能从任意节点启动 BFS。我认为你必须从任意节点到节点 s 执行 BFS,然后从 s 到 u 执行另一个 BFS。
    • 此外,它不适用于具有 13 个节点的树,其中每个非叶节点都有三个子节点。 For example
    • 按照算法,答案是 9 C 2 = 36,但实际答案是 3*6 + 3*3 = 27。
    【解决方案2】:

    是的,有一种自下而上的线性时间算法,类似于仅查找直径的算法。这是Java-ish伪代码中的签名;我将把算法本身留作练习。

    class Node {
        Collection<Node> children;
    }
    
    class Result {
        int height;  // height of the tree
        int num_deep_nodes;  // number of nodes whose depth equals the height
        int diameter;  // length of the longest path inside the tree
        int num_long_paths;  // number of pairs of nodes at distance |diameter|
    }
    
    Result computeNumberOfLongPaths(Node root);  // recursive
    

    【讨论】:

    • 对不起,我还是看不到算法。您能否详细介绍一下 computeNumberOfLongPaths()?
    猜你喜欢
    • 1970-01-01
    • 2011-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 2018-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多