不了解二叉树最大路径和问题的解决方案

62o28rlo  于 2021-07-03  发布在  Java
关注(0)|答案(3)|浏览(294)

Geeksforgeks网站为二叉树的最大路径和问题提供了一个解决方案。问题如下:
给定一棵二叉树,求最大路径和。路径可以从树中的任何节点开始和结束。
解决方案的核心如下:

int findMaxUtil(Node node, Res res) 
{ 

    if (node == null) 
        return 0; 

    // l and r store maximum path sum going through left and 
    // right child of root respectively 
    int l = findMaxUtil(node.left, res); 
    int r = findMaxUtil(node.right, res); 

    // Max path for parent call of root. This path must 
    // include at-most one child of root 
    int max_single = Math.max(Math.max(l, r) + node.data, 
                              node.data); 

    // Max Top represents the sum when the Node under 
    // consideration is the root of the maxsum path and no 
    // ancestors of root are there in max sum path 
    int max_top = Math.max(max_single, l + r + node.data); 

    // Store the Maximum Result. 
    res.val = Math.max(res.val, max_top); 

    return max_single; 
} 

int findMaxSum() { 
    return findMaxSum(root); 
 } 

// Returns maximum path sum in tree with given root 
int findMaxSum(Node node) { 

    // Initialize result 
    // int res2 = Integer.MIN_VALUE; 
    Res res = new Res(); 
    res.val = Integer.MIN_VALUE; 

    // Compute and return result 
    findMaxUtil(node, res); 
    return res.val; 
}
``` `Res` 具有以下定义:

class Res {
public int val;
}

我对这些代码背后的推理感到困惑:

int max_single = Math.max(Math.max(l, r) + node.data, node.data);

int max_top = Math.max(max_single, l + r + node.data);

res.val = Math.max(res.val, max_top);

return max_single;

我相信上面的代码遵循这个逻辑,但我不明白为什么这个逻辑是正确的或有效的:
对于每个节点,最大路径可以有四种方式通过节点:
仅节点
通过左子节点+节点的最大路径
通过右子节点+节点的最大路径
通过左子节点的最大路径+节点+通过右子节点的最大路径
特别是,我不明白为什么 `max_single` 正在函数中返回 `findMaxUtil` 当我们计算变量 `res.val` 包含我们感兴趣的答案。网站上给出了以下原因,但我不明白:
需要注意的一点是,每个子树的根需要返回最大路径和,这样最多只涉及根的一个子树。
有人能解释一下这个步骤吗?
bvjveswy

bvjveswy1#

特别是,我不明白当变量res.val包含我们感兴趣的答案时,为什么在函数findmaxutil中返回max\u single。
问题是 findMaxUtil() 真正做两件事:它返回它应用到的树的最大和,并更新一个变量来跟踪遇到的最大和。在原始代码中有这样一个注解,但您在问题中编辑了它,也许是为了简洁:

// This function returns overall maximum path sum in 'res' 
// And returns max path sum going through root. 
int findMaxUtil(Node node, Res res)

因为java按值传递参数,但是java中的每个对象变量都隐式引用实际对象,所以很容易忽略 Res 那已经过去了 res 此函数可以更改参数。这正是你问的台词中发生的事情:

int max_single = Math.max(Math.max(l, r) + node.data, node.data);  

int max_top = Math.max(max_single, l + r + node.data); 

res.val = Math.max(res.val, max_top); 

return max_single;

第一行找到节点本身的最大值或节点加上最大子树的最大值,结果就是 max path sum going through root . 在最后一行返回这个值是这个函数要做的一件事。第二行和第三行查看该值,并考虑该值或包含两个子项的路径是否大于先前看到的任何路径,如果大于,则更新该值 res ,这是该函数所做的另一件事。请记住 res 是存在于方法外部的某个对象,因此对它的更改将一直保留,直到递归停止并 findMaxSum(Node) ,开始了整个过程,返回 res.val .
所以,回到最上面的问题 findMaxUtil 退货 max_single 它使用该值递归地确定通过每个子树的最大路径。中的值 res 也进行了更新,以便 findMaxSum(Node) 我可以用它。

hsvhsicv

hsvhsicv2#

你错过了 res.val . 该算法试图探索整个树,使用 res.val 等于在此之前探索的最大路径长度。在每个步骤中,它递归地遍历子级并更新 res.val 最大路径长度,如果高于已经存在的路径长度。
证明:
假设您的算法适用于具有高度的树 n . 对于有高度的树 n+1 有一个根和两个子树的高度 n . 还要考虑一下 findMaxUtil 适用于 i<=n 并将返回最大路径,从子树的部分根开始。
所以你树上的最大路径 n+1 计算如下
findMaxUtil(subtree1) findMaxUtil(subtree2) findmaxUtil(subtree1)+root.data findmaxUtil(subtree2)+root.data findmaxUtil(subtree1)+findmaxUtil(subtree2)+root.data res.val 最后的结果是: findmaxUtil(newTree)=max(items 1:6) .

4c8rllxm

4c8rllxm3#

老实说,我认为网站上的描述很不清楚。我会尽我所能让你相信算法背后的道理。
我们有一个二叉树,节点上有值:

我们在树上寻找一条路径,一条连接节点的链。

由于它是一个有向树,任何非空路径都由一个最低深度节点(即路径中距离树的根最近的节点)组成,一个由零个或多个节点组成的路径下降到最低深度节点的左侧,一个由零个或多个节点组成的路径下降到最低深度节点的右侧。特别地,在树的某个地方有一个节点,它是最大路径中深度最低的节点(实际上,可能有多条这样的路径被绑定为相等的值,并且它们可能都有各自不同的最低深度节点。那很好。只要至少有一个,这才是最重要的。)
(我在图表中使用了“最高”,但我的意思是“最低深度”。要清楚的是,每当我使用“深度”或“下降”时,我指的是树中的位置。每当我使用“maximum”时,我指的是一个节点的值或路径中节点值的总和。)

因此,如果我们能找到它的最低深度节点,我们知道最大值路径是由节点本身,一个由零个或多个节点组成的从(包括)它的左子节点下降的子路径,和一个由零个或多个节点组成的从(包括)它的右子节点下降的子路径组成的。这是一个很小的步骤,以得出结论,左,右下降路径必须是最大值这样的下降路径在每一边(如果这一点不明显,请考虑您选择的任何其他路径,您可以通过选择该侧的最大值降序路径来增加总值。)如果其中一条或两条路径的值都为负值,则在负侧根本不包含任何节点。
所以我们有一个单独的子问题-给定一个子树,最大值路径通过它的根下降的值是多少?好吧,它可能只是根本身,如果根在它的子路径上的所有路径都有负和,或者如果它没有子路径。否则,它是根加上根在其子级上的任何一个的最大值降序路径。这个子问题可以很容易地自己解决,但是为了避免重复遍历和重做工作,我们将把它们结合到树的一次遍历中。
回到主要问题,我们知道一些节点是最大值路径中深度最低的节点。我们甚至不特别关心何时访问它—我们只是递归地访问每个节点,并找到最大值路径,该路径作为其最低深度节点,确保在某个点我们将访问我们想要的。在每个节点上,我们计算从该点开始的最大值路径和子树内的下降路径( max_single )以及最大值路径,对于该路径,该节点是路径中深度最低的节点( max_top ). 后者是通过取节点并“粘上”零来找到的,即通过其子节点的一条或两条最大降序路径(自 max_single 已经是从0或其中一个子级开始的最大值路径,我们需要考虑的唯一额外的事情是通过两个子级的路径。) max_top 在每个节点上保持 res.val ,我们保证在遍历完树时,将找到所有值中最大的值。在我们返回的每个节点 max_single 在父计算中使用。在算法的最后,我们从 res.val .

相关问题