【问题标题】:Longest path between 2 Nodes2个节点之间的最长路径
【发布时间】:2011-03-08 15:54:54
【问题描述】:

计算两个节点之间的最长路径。
路径在拱形中。
方法的签名是:

public static int longestPath(Node n)

在下面的示例二叉树中,它是 4(通过 2-3-13-5-2)。

这就是我现在拥有的,对于给定的树,它只返回 0。

public static int longestPath(Node n) {
    if (n != null) {
        longestPath(n, 0);
    }
    return 0;
}
private static int longestPath(Node n, int prevNodePath) {

    if (n != null && n.getLeftSon() != null && n.getRightSon() != null) {
        int currNodePath = countLeftNodes(n.getLeftSon()) + countRightNodes(n.getRightSon());
        int leftLongestPath = countLeftNodes(n.getLeftSon().getLeftSon()) + countRightNodes(n.getLeftSon().getRightSon());
        int rightLongestPath = countLeftNodes(n.getRightSon().getLeftSon()) + countRightNodes(n.getRightSon().getRightSon());

        int longestPath = currNodePath > leftLongestPath ? currNodePath : leftLongestPath;
        longestPath = longestPath > rightLongestPath ? longestPath : rightLongestPath;

        longestPath(n.getLeftSon(), longestPath);
        longestPath(n.getRightSon(), longestPath);

        return longestPath > prevNodePath ? longestPath : prevNodePath;
    }
    return 0;
}
private static int countLeftNodes(Node n) {
    if (n != null) {
        return 1+ countLeftNodes(n.getLeftSon());
    }
    return 0;
}
private static int countRightNodes(Node n) {
    if (n != null) {
        return 1+ countRightNodes(n.getRightSon());
    }
    return 0;
}

我知道我在某处遗漏了一个关键概念……当我尝试跟踪执行流程时,我的大脑发疯了……
我是否正确地说,通过在根、其左右节点之间找到最长路径,然后在其左右节点上递归,将它们从先前方法调用中传递给它们的最长路径,最后(何时?)返回最长路径,我不知道你是怎么退货的……

【问题讨论】:

  • 如何处理二叉树本质上是链表的情况?例如,2-3-13(13 是根,3 是 13 的左边,2 是 3 的左边)。结果应该是 2 吗?

标签: java algorithm recursion binary-tree


【解决方案1】:

也许就这么简单:

public static int longestPath(Node n) {
    if (n != null) {
        return longestPath(n, 0); // forgot return?
    }
    return 0;
}

它比人们乍一看可能想的要复杂。考虑以下树:

      1
     / \
    2   3
   / \
  4   5
 / \   \
6   7   8
   / \   \
  9   a   b

在这种情况下,根节点甚至不在最长路径中(a-7-4-2-5-8-b)。

因此,您必须执行以下操作:对于每个节点 n,您必须计算以下内容:

  • 从左子树的根开始计算左子树中的最长路径(称为L
  • 从右子树的根开始计算右子树中的最长路径(称为R
  • 计算左子树中最长的路径(不一定从左子树的根开始)(称为l
  • 计算右子树中最长的路径(不一定从右子树的根开始)(称为r

然后,决定哪种组合可以最大化路径长度:

  • L+R+2,即从左子树的子路径到当前节点,从当前节点通过右子树的子路径
  • l,即只取左子树并从路径中排除当前节点(以及右子树)
  • r,即只取右子树并从路径中排除当前节点(以及左子树)

所以我会做一些小技巧,对于每个节点,不只返回一个 int,而是返回一个包含 (L+R+2, l, r) 的整数的三元组。然后调用者必须根据上述规则决定如何处理这个结果。

【讨论】:

  • 谢谢,但我试过了,它只返回零。至于我忘记添加返回,我添加了它,它确实返回了 4,但它只考虑了从根节点延伸的拱......其他比它更长的拱被简单地忽略了。
  • 其实最长的路径是6(例如:a-7-4-2-5-8-b)。不过,这是一个很好的例子。
  • @Daniel 嗯嗯,我理解的路径是拱形(倒 V)。
  • @timeNomad:您可能需要向您的讲师寻求说明。 9-7-4-2-5-8-b 对我来说仍然是“拱门”。另见:en.wikipedia.org/wiki/Path_(graph_theory)
  • 为什么我们需要lr?我想不出L+R+2 不能解决问题的极端情况。
【解决方案2】:

正确的算法是:

  1. 从任何节点运行 DFS 以找到最远的叶节点。标记该节点 T。
  2. 运行另一个 DFS 以查找距离 T 最远的节点。
  3. 您在第 2 步中找到的路径是树中最长的路径。

这个算法肯定会起作用,而且你也不仅仅局限于二叉树。我不确定你的算法:

我说得对吗,通过在根、它的左右节点之间找到最长的路径,然后在它的左右节点上递归,将前一个方法调用的最长路径传递给它们,最后(何时???)返回最长的路径,我不确定你如何返回它......

因为我不明白你到底在描述什么。您可以手动处理一个示例或尝试更好地解释它吗?这样你可能会得到更好的帮助来理解它是否正确。

您似乎正在尝试递归实现基本相同的东西,只是为二叉树简化了。但是,对于这个问题,您的代码似乎相当复杂。查看讨论 here 以获得更简单的实现。

【讨论】:

  • 关于蜜蜂不仅限于二叉树:看看:en.wikipedia.org/wiki/Longest_path_problem 这表明问题(至少对于任意图)是 np-complete。
  • @phimuemue - 是的,我的意思是只要图形仍然是一棵树。
  • +1:这正确地找到了无向树中最长的路径(我想它也是树的直径)。
  • 1.找到一个循环。 2. 输入。 3. ... 4. 利润!
  • 1.在树中找到一个循环。 2. 向新手图论者出售带有循环的神奇树。 3. 利润。
【解决方案3】:

我认为你把事情复杂化了。

考虑通过节点 n 并且不到达 n 的父节点的最长路径。该路径的长度与连接到 n 的两个子图的高度之间有什么关系?

弄清楚之后,检查树递归推理如下:

以n为根的子树的最长路径是以下三个中最长的路径:

  1. 子树中最长的路径,根为n.left_child
  2. 子树中最长的路径,根为n.right_child
  3. 最长的路径,通过节点 n 并且不到达 n 的父节点

【讨论】:

    【解决方案4】:

    如果,对于每个节点 n,您的目标是计算这两个数字:

    • f(n):以n为根的树中最长路径的长度
    • h(n):以n为根的树的高度。

    对于每个终端节点(有null左右节点的节点),很明显f和h都是0。

    现在,每个节点n的h为:

    • 0 如果n.leftn.right 都是null
    • 1 + h(n.left) 如果只有n.left 不是null
    • 1 + h(n.right) 如果只有n.right 不是null
    • 1 + max(h(n.left), h(n.right)) 如果n.leftn.right 都不是null

    而 f(n) 是:

    • 0 如果n.leftn.right 都是null
    • max(f(n.left), h(n)) 如果只有n.left 不是null
    • ??如果只有n.right 不是null
    • ??如果n.leftn.right 都不是null

    (你需要弄清楚是什么替换了两个“??”占位符。有一些选择可以使这个策略奏效。我已经亲自测试过。)

    那么,longestPath(Node n) 就是 f(n):

    public class SO3124566
    {
        static class Node
        {
            Node left, right;
    
            public Node()
            {
                this(null, null);
            }
    
            public Node(Node left, Node right)
            {
                this.left = left;
                this.right = right;
            }
        }
    
        static int h(Node n)
        {
            // ...
        }
    
        static int f(Node n)
        {
            // ...
        }
    
        public static int longestPath(Node n)
        {
            return f(n);
        }
    
        public static void main(String[] args)
        {
            { // @phimuemue's example
                Node n6 = new Node(),
                    n9 = new Node(),
                    a = new Node(),
                    n7 = new Node(n9, a),
                    n4 = new Node(n6, n7),
                    b = new Node(),
                    n8 = new Node(null, b),
                    n5 = new Node(null, n8),
                    n2 = new Node(n4, n5),
                    n3 = new Node(),
                    n1 = new Node(n2, n3);
                assert(longestPath(n1) == 6);
            }{ // @Daniel Trebbien's example: http://pastebin.org/360444
                Node k = new Node(),
                    j = new Node(k, null),
                    g = new Node(),
                    h = new Node(),
                    f = new Node(g, h),
                    e = new Node(f, null),
                    d = new Node(e, null),
                    c = new Node(d, null),
                    i = new Node(),
                    b = new Node(c, i),
                    a = new Node(j, b);
                assert(longestPath(a) == 8);
            }
    
    
    
            assert(false); // just to make sure that assertions are enabled.
                // An `AssertionError` is expected on the previous line only.
        }
    }
    

    您应该能够编写 f 和 h 的递归实现来使这段代码工作;但是,这种解决方案效率极低。它的目的只是为了理解计算。

    为了提高效率,您可以使用memoization 或将其转换为使用堆栈的非递归计算。

    【讨论】:

      【解决方案5】:

      嗯,嗯,如果我正确理解了您的问题,这是我的解决方案 [但在 C++ 中(对不起)]:

      int h(const Node<T> *root)
      {
          if (!root)
              return 0;
          else
              return max(1+h(root->left), 1+h(root->right));
      }
      
      void longestPath(const Node<T> *root, int &max)
      {
          if (!root)
              return;
          int current = h(root->left) + h(root->right) + 1;
          if (current > max) {
              max = current;
          }
          longestPath(root->left, max);
          longestPath(root->right, max);
      }
      
      int longest()
      {
          int max = 0;
          longestPath(root, max);
          return max;
      }
      

      【讨论】:

        【解决方案6】:

        这是我在 C++ 中的递归解决方案:

        int longest_dis(Node* root) {
            int height1, height2;
        
            if( root==NULL)
                return 0;
        
            if( root->left == NULL ) && ( root->right == NULL )
                return 0;
        
            height1 = height(root->left); // height(Node* node) returns the height of a tree rooted at node
            height2 = height(root->right);
        
            if( root->left != NULL ) && ( root->right == NULL )
                return max(height1+1, longest_dis(root->left) );
        
            if( root->left == NULL ) && ( root->right != NULL )
                return max(height2+1, longest_dis(root->right) );
        
            return max(height1+height2+2, longest_dis(root->left), longestdis(root->right) );
        }
        

        【讨论】:

          【解决方案7】:
          public int longestPath() {
              int[] result = longestPath(root);
              return result[0] > result[1] ? result[0] : result[1];
          }
          
          // int[] {self-contained, root-to-leaf}
          private int[] longestPath(BinaryTreeNode n) {
              if (n == null) {
                  return new int[] { 0, 0 };
              }
              int[] left = longestPath(n.left);
              int[] right = longestPath(n.right);
          
              return new int[] { Util.max(left[0], right[0], left[1] + right[1] + 1),
                      Util.max(left[1], right[1]) + 1 };
          }
          

          【讨论】:

            【解决方案8】:

            简单实现:

            int maxDepth(Node root) {
                if(root == null) {
                    return 0;
                } else {
                    int ldepth = maxDepth(root.left);
                    int rdepth = maxDepth(root.right);
                    return ldepth>rdepth ? ldepth+1 : rdepth+1;
                }
            }
            
            int longestPath(Node root)
            {
               if (root == null)
                 return 0;
            
              int ldepth = maxDepth(root.left);
              int rdepth = maxDepth(root.right);
            
              int lLongPath = longestPath(root.left);
              int rLongPath = longestPath(root.right);
            
              return max(ldepth + rdepth + 1, max(lLongPath, rLongPath));
            }
            

            【讨论】:

            • 这将是 o(n^2) 。 o(n)应该有更好的解
            【解决方案9】:

            考虑到@phimuemue 示例和@IVlad 解决方案,我决定自己检查一下,所以这是我在python 中实现@IVlad 解决方案:

            def longestPath(graph,start, path=[]):
                nodes = {}
                path=path+[start]
                for node in graph[start]:
                    if node not in path:
                        deepestNode,maxdepth,maxpath = longestPath(graph,node,path)
                        nodes[node] = (deepestNode,maxdepth,maxpath)
                maxdepth = -1
                deepestNode = start
                maxpath = []
                for k,v in nodes.iteritems():
                    if v[1] > maxdepth:
                        deepestNode = v[0]
                        maxdepth = v[1]
                        maxpath = v[2]
                return deepestNode,maxdepth +1,maxpath+[start]
            
            if __name__ == '__main__':
                graph = { '1' : ['2','3'],
                          '2' : ['1','4','5'],
                          '3' : ['1'],
                          '4' : ['2','6','7'],
                          '5' : ['2','8'],
                          '6' : ['4'],
                          '7' : ['4','9','a'],
                          '8' : ['5','b'],
                          '9' : ['7'],
                          'a' : ['7'],
                          'b' : ['8']
                }
                """
                      1
                     / \
                    2   3
                   / \
                  4   5
                 / \   \
                6   7   8
                   / \   \
                  9   a   b
                """
                deepestNode,maxdepth,maxpath = longestPath(graph,'1')
                print longestPath(graph, deepestNode)
            
            >>> ('9', 6, ['9', '7', '4', '2', '5', '8', 'b'])
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-03-08
              • 1970-01-01
              • 2012-06-21
              • 2010-11-18
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多