思路:
首先我们先写一个函数maxnodepath,这个函数的作用是返回以某一个节点为根节点时,从该节点到叶子节点的最大路径和。比如例1中,节点1的maxnodepath的返回值为1+3=4。
当这个节点为null,则返回0。
当这个节点为叶子节点(左子树和右子树都为null),则返回该节点的值。
当这个节点不为叶子节点,则返回该节点的值+左右子树的maxnodepath的最大值。
但是这样并不能满足我们的要求。我们的要求并不是找出从根节点开始的一条最大路径。如示例2所示,最大路径并不一定要经过根节点。它有可能是在遍历过程中的一个中间状态,那么我们就需要在递归过程中,设立一个变量来记录这个中间状态,并记录下来中间状态的最大值。
当我们遍历到一个节点时,我们只看该节点为根节点的子树。这个子树的最大路径和有可能为:
- 根节点
- 左子树最大路径和
- 右子树最大路径和
- 从根节点开始的最大路径和(根节点+max(左子树最大路径和,右子树最大路径和))
- 根节点+左子树最大路径和+右子树最大路径和
其中4和5我们都可以通过2和3得到。
这5个就是我们需要记录的中间状态。我们设置一个全局遍历max,每次遍历到一个节点的时候,我们就比较这5个中间状态和max,选取最大的一个更新max。这样当我们把所有节点都遍历完以后,max就会记录下来遍历过程中最大的那个路径和。
还要注意的一点是,某个节点左子树和右子树的最大路径和不能小于0。如果返回值小于0就要强行置为0。因为如果某个节点的左子树的最大路径和是小于0的,比如2 -1 -2. 那么我们就将左右子树抛弃。我们不一定非要走左右子树啊。所以2 -1 -2 的最大路径和就是2。如果不这样做的话,我们看下面的一个例子:
这棵树的根节点是2,我们可以直接看出来这棵树的最大路径和是4。但是如果我们遍历子树的时候,子树的最大路径和可以小于0,那我们可以从头到尾走一遍。我们首先走到了根节点2,它不是叶子节点,于是我们就要求其左右子树的最大路径和。其右子树的maxnodepath返回值就是0。
然后看左子树2。左子树2的左右节点都是叶子节点,所以左子树的maxnodepath返回值就是2±6=-4。
我们可以写出5个中间状态
- 根节点=2
- 左子树最大路径和=-6
- 右子树最大路径和=-6
- 从根节点开始的最大路径和(根节点+max(左子树最大路径和,右子树最大路径和))=2-6= -4
- 根节点+左子树最大路径和+右子树最大路径和 2-6-6 = -10
此时max=2。maxnodepath返回值=-4
然后我们回到了根节点,其左右子树的maxnodepath返回值一个是0,一个是-4。那么我们可以写出5个中间状态: - 根节点=2
- 左子树最大路径和=-4
- 右子树最大路径和=0
- 从根节点开始的最大路径和(根节点+max(左子树最大路径和,右子树最大路径和))=2+0 = 2
- 根节点+左子树最大路径和+右子树最大路径和 2+0-4 = -2
这时max = 2,我们最后得到的结果就是2。
我们来看如果让节点左子树和右子树的最大路径和不能小于0,看看是不是正确结果:
我们首先走到了根节点2,它不是叶子节点,于是我们就要求其左右子树的最大路径和。其右子树的maxnodepath返回值就是0。
然后看左子树2。左子树2的左右节点都是叶子节点,所以左子树的maxnodepath返回值就是2±6=-4。
我们可以写出5个中间状态
- 根节点=2
- 左子树最大路径和=0(-6小于0,强制置为0)
- 右子树最大路径和=0(-6小于0,强制置为0)
- 从根节点开始的最大路径和(根节点+max(左子树最大路径和,右子树最大路径和))=2+0= 2
- 根节点+左子树最大路径和+右子树最大路径和 2+0+0=2
此时max=2。maxnodepath返回值=2
然后我们回到了根节点,其左右子树的maxnodepath返回值一个是0,一个是-4。那么我们可以写出5个中间状态: - 根节点=2
- 左子树最大路径和=2(不小于0,不变)
- 右子树最大路径和=0(不小于0,不变)
- 从根节点开始的最大路径和(根节点+max(左子树最大路径和,右子树最大路径和))=2+2= 4
- 根节点+左子树最大路径和+右子树最大路径和 2+2=0= 4
这时max = 4,我们最后得到的结果就是4。这次我们可以发现,得到的max就是最大路径和4。
class Solution {
int maxSum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxGain(root);
return maxSum;
}
public int maxGain(TreeNode node) {
if (node == null) {
return 0;
}
// 递归计算左右子节点的最大贡献值
// 只有在最大贡献值大于 0 时,才会选取对应子节点
int leftGain = Math.max(maxGain(node.left), 0);
int rightGain = Math.max(maxGain(node.right), 0);
// 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
int priceNewpath = node.val + leftGain + rightGain;
// 更新答案
maxSum = Math.max(maxSum, priceNewpath);
// 返回节点的最大贡献值
return node.val + Math.max(leftGain, rightGain);
}
}