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

2022-09-04 21:18:38

网站GeeksforGeeks为二叉树的最大路径总和问题提出了一个解决方案。问题如下:

给定一个二叉树,找到最大路径总和。路径可以在树中的任何节点开始和结束。

该解决方案的核心如下:

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; 

我相信上面的代码遵循这个逻辑,但我不明白为什么这个逻辑是正确的或有效的

对于每个节点,最大路径可以通过四种方式通过节点:

  1. 仅节点
  2. 通过左子节点 + 节点的最大路径
  3. 通过右子节点 + 节点的最大路径
  4. 通过左子节点 + 节点的最大路径 + 通过右子节点的最大路径

特别是,我不明白为什么当我们变量包含我们感兴趣的答案时,函数中会返回。网站上给出了以下原因,但我不明白:max_singlefindMaxUtilres.val

需要注意的重要一点是,每个子树的根都需要返回最大路径和,以便最多涉及根的一个子级。

有人可以为解决方案的这一步提供解释吗?


答案 1

特别是,我不明白为什么当我们变量res.val包含我们感兴趣的答案时,为什么在函数findMaxUtil中返回max_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 中的每个对象变量都隐式引用实际对象,因此很容易忽略这样一个事实,即此函数可能会更改参数中传递的参数。这正是你问的几行中发生的事情:Resres

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 rootresresfindMaxSum(Node)res.val

因此,回到顶部的问题,返回的原因是它使用该值以递归方式确定通过每个子树的最大路径。中的值也会更新,以便可以使用它。findMaxUtilmax_singleresfindMaxSum(Node)


答案 2

您缺少 的值。该算法试图探索整个树,使用等于迄今为止探索的最大路径长度。在每个步骤中,它以递归方式遍历子级,并使用最大路径长度(如果大于已存在的路径长度)进行更新。res.valres.valres.val

证明:

假设您的算法适用于高度为 的树。对于高度的树木,有一个根和2个高度的子树。还要考虑,这适用于并将返回最大路径,从子树的部分根开始。nn+1nfindMaxUtili<=n

因此,树中具有高度的最大路径计算如下n+1

  1. findMaxUtil(subtree1)
  2. findMaxUtil(subtree2)
  3. findmaxUtil(subtree1)+root.data
  4. findmaxUtil(subtree2)+root.data
  5. findmaxUtil(subtree1)+findmaxUtil(subtree2)+root.data
  6. res.val

最后结果是:.findmaxUtil(newTree)=max(items 1:6)