求二叉树的最大路径和是一道蛮有难度的题,难就难在起始位置和结束位置可以为任意位置,我当然是又不会了,于是上网看看大神们的解法,像这种类似数的遍历的题,一般来说都需要用DFS来求解,我们先来看一个简单的例子:
由于这是一个很简单的例子,我们很容易就能找到最长路径为7-11-4-13,那么怎么用递归来找出正确的路径和呢?根据以往的经验,树的递归解法一般都是递归到叶节点,然后开始边处理边回溯到根节点。那么我们就假设此时已经递归到结点7了,那么其没有左右子节点,所以如果以结点7为根结点的子树最大路径和就是7。然后回溯到结点11,如果以结点11为根结点的子树,我们知道最大路径和为7+11+2=20。但是当回溯到结点4的时候,对于结点11来说,就不能同时取两条路径了,只能取左路径,或者是右路径,所以当根结点是4的时候,那么结点11只能取其左子结点7,因为7大于2。所以,对于每个结点来说,我们要知道经过其左子结点的path之和大还是经过右子节点的path之和大。那么我们的递归函数返回值就可以定义为以当前结点为根结点,到叶节点的最大路径之和,然后全局路径最大值放在参数中,用结果res来表示。
在递归函数中,如果当前结点不存在,那么直接返回0。否则就分别对其左右子节点调用递归函数,由于路径和有可能为负数,而我们当然不希望加上负的路径和,所以我们和0相比,取较大的那个,就是要么不加,加就要加正数。然后我们来更新全局最大值结果res,就是以左子结点为终点的最大path之和加上以右子结点为终点的最大path之和,还要加上当前结点值,这样就组成了一个条完整的路径。而我们返回值是取left和right中的较大值加上当前结点值,因为我们返回值的定义是以当前结点为终点的path之和,所以只能取left和right中较大的那个值,而不是两个都要,参见代码如下:
public class solution{
public int maxPathSum(TreeNode root) {
max_sum = Integer.MIN_VALUE;
//错误写法:如果定义递归时直接把max_sum当做参数传递进去则结果一直是Integer.MIN_VALUE.
//因为Java是值传递,dfs拿到的是max_sum的副本,对它进行修改并不会影响到全局的max_sum.
//dfs(root,max_sum);
dfs(root);
return max_sum;
}
//维护一个全局变量max_sum,且没有把它当做参数直接传进去,在dfs方法中对max_sum的修改和赋值会直接影响到max_sum的值
private int max_sum;
private int dfs(TreeNode root) {
if (root == null) return 0;
int l = dfs(root.left);
int r = dfs(root.right);
int sum = root.val;
if (l > 0) sum += l;
if (r > 0) sum += r;
max_sum = Math.max(max_sum, sum);
return Math.max(r, l) > 0 ? Math.max(r, l) + root.val : root.val;
}
}
我们应该尽量避免使用全局变量,所以这里其实可以用一个ArrayList对象来代替全局变量,代码如下:
public int maxPathSum(TreeNode root) {
if(root==null)
return 0;
//维护一个ArrayList(避免使用全局变量),但其实只用到第一个位置
ArrayList<Integer> res = new ArrayList<Integer>();
res.add(Integer.MIN_VALUE);
helper(root,res);
return res.get(0);
}
private int helper(TreeNode root, ArrayList<Integer> res)
{
if(root == null)
return 0;
int left = helper(root.left, res);
int right = helper(root.right, res);
int cur = root.val + (left>0?left:0)+(right>0?right:0);
if(cur>res.get(0))
res.set(0,cur);
return root.val+Math.max(left, Math.max(right,0));
}