2个节点之间的最长路径
计算两个节点之间的最长路径。
路径是拱形的。
签名方法是:
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; }
我知道我在某个地方错过了一个关键概念……当我尝试跟踪执行流程时,我的大脑发疯了……
我是这么说的,通过找到根中最长的路径,它的左右节点,然后递归到它的左右节点,传递它们从前一个方法调用的最长路径,最后(当?)返回最长的路径,我我不确定你是怎么回事的……
也许它就是这么简单:
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 ab
在这种情况下,根节点甚至不在最长路径中( a-7-4-2-5-8-b
)。
因此,您必须执行以下操作:对于每个节点n
您必须计算以下内容:
- 从左子树的根开始计算左子树中的最长路径(称为
L
) - 从右子树的根开始计算右子树中的最长路径(称为
R
) - 计算左子树中的最长路径(不一定以左子树的根开始)(称为
l
) - 计算右子树中最长的路径(不一定以右子树的根开始)(称为
r
)
然后,决定哪个组合最大化路径长度:
-
L+R+2
,即从左子树中的子路径到当前节点,从当前节点到右子树中的子路径 -
l
,即只取左子树并从路径中排除当前节点(从而右子树) -
r
,即只取右子树并从路径中排除当前节点(从而保留左子树)
所以我会做一点点破解,并且每个节点都不会返回一个int
,而是包含(L+R+2, l, r)
的三个整数。 然后,呼叫者必须根据上述规则决定如何处理此结果。
一个正确的算法是:
- 从任何节点运行DFS以查找最远的叶节点。 标记节点T.
- 运行另一个DFS以从T找到最远的节点。
- 您在步骤2中找到的路径是树中最长的路径。
这个算法肯定会起作用,你也不仅限于二叉树。 我不确定你的算法:
我是这么说的,通过找到根中的最长路径,它的左右节点,然后递归它的左右节点,传递它们从前一个方法调用的最长路径,最后(当???)返回最长的路径,我不确定你是怎么回事的……
因为我不明白你究竟在描述什么。 你可以手工制作一个例子或尝试更好地解释它吗? 这样你可以更好地帮助理解它是否正确。
您似乎正在尝试递归实现基本相同的事情,只是为二叉树简化。 但是,对于这个问题,您的代码似乎相当复杂 请查看此处的讨论以获得更简单的实现。
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 }; }
简单实施:
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)); }
这是我在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) ); }
我觉得你太复杂了。
想想通过节点n的最长路径并且不会到达n的父节点。 该路径的长度与连接到n的两个子区的高度之间的关系是什么?
搞清楚之后,检查树递归推理如下:
具有根n的子树的最长路径是以下三个中的最长路径:
- 子树中最长的路径,其根目录为n.left_child
- 子树中最长的路径,其根目录为n.right_child
- 最长的路径,通过节点n并且不会到达n的父节点
如果,对于每个节点n
,您的目标是计算这两个数字:
- f(
n
):以n
为根的树中最长路径的长度 - h(
n
):以n
为根的树的高度。
对于每个终端节点(具有null
左和右节点的节点),显然f和h都是0。
现在,每个节点n
的h是:
- 如果
n.left
和n.right
都为null
,n.right
0 - 如果只有
n.left
为非null
n.left
1 + h(n.left
) - 如果只有
n.right
为非null
n.right
1 + h(n.right
) - 1 + max(h(
n.left
),h(n.right
))如果n.left
和n.right
都是非null
而f( n
)是:
- 如果
n.left
和n.right
都为null
,n.right
0 - max(f(
n.left
),h(n
))如果只有n.left
是非null
- ?? 如果只有
n.right
是非null
- ?? 如果
n.left
和n.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或将其转换为使用堆栈的非递归计算。
好吧,嗯,如果我正确理解你的问题,这是我的解决方案[但在C ++中(对不起)]:
int h(const Node *root) { if (!root) return 0; else return max(1+h(root->left), 1+h(root->right)); } void longestPath(const Node *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; }
考虑到@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 ab """ deepestNode,maxdepth,maxpath = longestPath(graph,'1') print longestPath(graph, deepestNode) >>> ('9', 6, ['9', '7', '4', '2', '5', '8', 'b'])