题目
给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
分析
当我看到这道题的时候,我的第一反应使用BFS,每次记录当前节点的当前最大路径和,然后定义同一个全局变量来保存出现过的最大路径和。
也就是最大子序列和+树的层次遍历。
那于是说干就干,写如下代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxPathSum(TreeNode root) {
if(root == null)
return 0;
//存放节点的队列
Deque<TreeNode> queueNode = new LinkedList<>();
//存放当当前最大路径和,如果当前和小于0,那么取当前节点的值,否则相加
Deque<Integer> queueCurSum = new LinkedList<>();
int max = root.val;
queueNode.addLast(root);
queueCurSum.addLast(root.val);
while(!queueNode.isEmpty()){
TreeNode node = queueNode.removeFirst();
int curSum = queueCurSum.removeFirst();
max = Math.max(max,curSum);
if(node.left != null){
queueNode.addLast(node.left);
queueCurSum.addLast(curSum < 0? node.left.val : curSum+node.left.val);
}
if(node.right != null){
queueNode.addLast(node.right);
queueCurSum.addLast(curSum < 0? node.right.val : curSum+node.right.val);
}
}
return max;
}
}
万万没想到
自信满满进行测试,结果没想到这样的。
我仔细一看,最大路径和不就是1+3吗?怎么会是6呢?
再一看题目,原来题目里的路径指的是从任意节点到节点,而不是一般定义的从某个节点出发,到达他的子节点。
好吧,那怎么做呢?
仔细思考一下,给定一个节点,如何判断经过这个节点的最大路径呢?
我们以最简单的示例二叉树为例。
给出二叉树[1,2,3]。
对于根节点1来说,经过他的路径一共有三条:2->1->3,2->1,3->1.
也就说经过这点的路径和取决于它的两个子树的路径和以及他本身。
想到这一点我们就不难得出方程:
PathSum = max{node.val+node.val.left,node.val+node.right.val+node.val+node.left.val+node.right.val}
但是考虑到,如果子节点的值是负的,那么没有必要从子节点出发了,那么子节点的路径和就定义为max(0,node。left.val)
。
这边要注意的是对于当前节点来说,最大路径和是上面给出的方程,但是如果该节点的最大路径和是从左子节点走到右子节点的话,这个最大路径和对于其父节点是没有意义的。也就是说虽然我们记录的时候记录的最大路径和,但是返回给上一个节点的值是,当前节点加上左子树和右子树的最大值。
最后再定义一个全局变量保存最大的路径和。
代码如下:
class Solution {
int max;
public int maxPathSum(TreeNode root) {
if(root == null)
return 0;
max = Integer.MIN_VALUE;
getSum(root);
return max;
}
public int getSum(TreeNode node){
if(node == null)
return 0;
int leftSum = Math.max(0,getSum(node.left));
int rightSum = Math.max(0,getSum(node.right));
int curSum = Math.max(node.val+leftSum,Math.max(node.val+rightSum,node.val+rightSum+leftSum));
max = Math.max(max,curSum);
//注意这里,返回给上一个节点的值只能是当前节点的右子树和左子树的最大值加上当前节点。
//而非实际的最大路径和。
return node.val + Math.max(leftSum,rightSum);
}
}
后记
这个故事告诉我们,看到路径不要理所当然的以为就是从节点出发到叶子节点了,还是要看看题目怎么说的,不要想当然。