leetcode 124 二叉树中的最大路径和
没看懂题目什么意思,先从群里大佬那里偷来个思路研究研究
借鉴地
思路
对于二叉树的问题,可以将其抽象成一种简单的结构,即:
根
/ \
左子树 右子树
对于这道题目同样如此,题目要求任意节点出发到达任意节点的最大路径和。根据上面抽象出来的结果,我们最多能够得到三条满足题意的路径:
- 左子树最大路径和 + 根
- 右子树最大路径和 + 根
- 左子树 + 根 + 右子树
这就是所有的路径了, 按照题目求出他们之间的最大值即是题目的结果。
不过需要注意的一点是,如果左右子树的最大路径和是负数,那么还不如不加他们呢。
对于第一种和第二种情况,都可以通过递归进行计算,但是第三种情况,我们是没办法进行递归的,所以在每次的方法体中,都要对其进行一次判断。
为什么不能划分出单独的左子树、右子树、根节点这样的路径呢?这是因为:
我们给出的树是一个抽象出来的树,它是递归的,实际上运行的时候我们可以通过后序遍历,会先计算出左右子树上最大路径和,单独的左右子树的可能在递归的时候已经被计算到了,所以就不需要再考虑
而对于根节点而言,如果左右子树的最大路径和不为负数,那当然要加它们了,必然比自己大,而如果左右子树的最大路径和为负数,那么就不会加它们,也就相当于单独的根节点了。
以上是大致思路。
代码
进入代码编写环节,首先要明确我们是要用递归来做这道题目的,然后就要进行递归三部曲。
- 确定递归的参数与返回值
- 确定递归体的内容
- 确定递归的终止条件
递归的参数与返回值
是先要明确,我们的递归函数是要用来求子树+根节点的最大路径和,所以返回值应该是一个int类型的变量。而传入参数只需要一个root即可。
所以函数签名可以确定好了:int maxPath(TreeNode)
递归体的内容
我们在递归的时候要做什么呢?
首先要进行一个后序遍历,即求出左右子树的最大路径和,就需要递归的调用本函数。
int left = maxPath(root.left);
int right = maxPath(root.right);
left和right就是左右子树的最大路径和。
这个明确了以后就可以计算 根 + 左子树 和 根 + 右子树中的最大路径和了。
只不过有一点需要注意,当左右子树的值为负数的情况下,要舍弃掉它们。
int maxChildVal = root.val +Math.max(0, Math.max(left,right));
这个maxChildVal
就是当前树的最大路径和,之后要返回的就是它。
接下来计算 根节点 + 左子树 + 右子树 的值,同样叶是如果左右子树最大路径和小于0就不加它们,相当于+0了
int rootVal = root.val + Math.max(0, Math.max(left,0)+ Math.max(right,0));
接下来要看究竟哪个值才是当前递归树的最大子树和了
就要进行一次比较
int max = Math.max(rootVal,maxChildVal)
max就是当前树下的最大路径和。不过这也只是局部的,它在局部最大并不代表在全局最大呀,此时就需要定义一个全局变量res,它可以保存全局的最大路径和。
res = Math.max(res,max)
比较之前记录好的最大路径和,与当前的最大路径和究竟谁大,然后保存大的那个。
最后比较结束,返回maxChildVal
递归终止条件
本题的终止条件很简单,就是递归到了最后的空结点。
if(root == null) retrun 0;
复杂度分析
时间复杂度:该算法将所有的结点都遍历到了,时间复杂度为O(n)
空间复杂度:我们需要一个大小与树等高的栈开销,因为要递归。所以空间复杂度为O(log(N))
下面是详细代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
// 对于一棵树而言,它的最大路径和有三种可能。一颗无论多大的树可以看作是这样的抽象
// a
// /\
// b c
// 可能是a + b + c
// 可能是a + b + (a父节点)
// 可能是a + b + (a父节点)
// 对于 a + b + c 而言,它不能够被递归,已经是最终值了。
// 对于 a + b 或 a + c 而言,它还有可能更大 因为a也可能有一个为正数的父节点,因此我们可以递归求最大的左右子树。
// 对于a + b 、 a + c 、 a + b + c这三者,我们要比较一下究竟谁最大,最大者就是当前树的环境下的最大者。如果还有父节点就继续递归求解。
// 不过可能会存在负值,加一个负值还不如不加,所以判断是否比0小,如果小这个值就取0。
// 但是对于a点而言,它是不允许被舍弃的,因为无论那种情况下,都需要它作为联络点。
class Solution {
int res = Integer.MIN_VALUE;
// 求二叉树的最大路径和
public int maxPathSum(TreeNode root) {
maxPath(root);
return res;
}
int maxPath(TreeNode root)
{
if(root == null)
{
return 0;
}
int left = maxPath(root.left);
int right = maxPath(root.right);
// 先求出要返回的子树值,即a + b 和 a + c两者最大值,当然,如果双方都小于0,那么就返回一个0表示路径不会包括这棵子树的节点。
int maxChildVal = root.val +Math.max(0, Math.max(left,right));
// 这是a + b + c 的值
int rootVal = root.val + Math.max(0, Math.max(left,0)+ Math.max(right,0));
// 比较双方的大小,取大者与返回值比较。
res = Math.max(res,Math.max(rootVal,maxChildVal));
return maxChildVal;
}
}