-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c8df410
commit c65481d
Showing
6 changed files
with
295 additions
and
2 deletions.
There are no files selected for viewing
91 changes: 91 additions & 0 deletions
91
...-learning/src/main/java/com/jacobs/basic/leetcode/editor/cn/BinaryTreeMaximumPathSum.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
//路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不 | ||
//一定经过根节点。 | ||
// | ||
// 路径和 是路径中各节点值的总和。 | ||
// | ||
// 给你一个二叉树的根节点 root ,返回其 最大路径和 。 | ||
// | ||
// | ||
// | ||
// 示例 1: | ||
// | ||
// | ||
//输入:root = [1,2,3] | ||
//输出:6 | ||
//解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6 | ||
// | ||
// 示例 2: | ||
// | ||
// | ||
//输入:root = [-10,9,20,null,null,15,7] | ||
//输出:42 | ||
//解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42 | ||
// | ||
// | ||
// | ||
// | ||
// 提示: | ||
// | ||
// | ||
// 树中节点数目范围是 [1, 3 * 104] | ||
// -1000 <= Node.val <= 1000 | ||
// | ||
// Related Topics 树 深度优先搜索 动态规划 二叉树 | ||
// 👍 1393 👎 0 | ||
|
||
|
||
package com.jacobs.basic.leetcode.editor.cn; | ||
|
||
import com.jacobs.basic.algorithm.TreeNode; | ||
|
||
public class BinaryTreeMaximumPathSum { | ||
public static void main(String[] args) { | ||
Solution solution = new BinaryTreeMaximumPathSum().new Solution(); | ||
} | ||
//leetcode submit region begin(Prohibit modification and deletion) | ||
|
||
/** | ||
* Definition for a binary tree node. | ||
* public class TreeNode { | ||
* int val; | ||
* TreeNode left; | ||
* TreeNode right; | ||
* TreeNode() {} | ||
* TreeNode(int val) { this.val = val; } | ||
* TreeNode(int val, TreeNode left, TreeNode right) { | ||
* this.val = val; | ||
* this.left = left; | ||
* this.right = right; | ||
* } | ||
* } | ||
*/ | ||
class Solution { | ||
// 维护一个全局的变量,用于更新以各个节点为根节点的最大路径和 | ||
int maxResult = Integer.MIN_VALUE; | ||
|
||
// 二叉树的后序遍历搞定 | ||
public int maxPathSum(TreeNode root) { | ||
maxPathSumHelper(root); | ||
return maxResult; | ||
} | ||
|
||
public int maxPathSumHelper(TreeNode root) { | ||
if (root == null) { | ||
return 0; | ||
} | ||
// 分为左右两子树的情况 | ||
// 0表示,若左子树结果为正,则记入贡献中,否则不计入,即为0 | ||
int left = Math.max(0, maxPathSumHelper(root.left)); | ||
int right = Math.max(0, maxPathSumHelper(root.right)); | ||
|
||
//以当前节点为根节点从下延伸得到的结果 | ||
int current = left + right + root.val; | ||
// 更新当前最新的值 | ||
maxResult = Math.max(maxResult, current); | ||
//TODO 要搞明白返回给上层则只能选择左边或者右边这一条路径,而不是三种情况 !!!! | ||
return Math.max(left, right) + root.val; | ||
} | ||
} | ||
//leetcode submit region end(Prohibit modification and deletion) | ||
|
||
} |
2 changes: 1 addition & 1 deletion
2
basic-learning/src/main/java/com/jacobs/basic/leetcode/editor/cn/all.json
Large diffs are not rendered by default.
Oops, something went wrong.
202 changes: 202 additions & 0 deletions
202
...ic/leetcode/editor/cn/doc/er-cha-shu-zhong-de-zui-da-lu-jing-he-by-leetcode-.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
### 📺 视频题解 | ||
![124. 二叉树中的最大路径.mp4](0fc716bb-576b-482f-8ec4-a1659b427b4f) | ||
|
||
### 📖 文字题解 | ||
#### 方法一:递归 | ||
|
||
首先,考虑实现一个简化的函数 `maxGain(node)`,该函数计算二叉树中的一个节点的最大贡献值,具体而言,就是在以该节点为根节点的子树中寻找以该节点为起点的一条路径,使得该路径上的节点值之和最大。 | ||
|
||
具体而言,该函数的计算如下。 | ||
|
||
- 空节点的最大贡献值等于 *0*。 | ||
|
||
- 非空节点的最大贡献值等于节点值与其子节点中的最大贡献值之和(对于叶节点而言,最大贡献值等于节点值)。 | ||
|
||
例如,考虑如下二叉树。 | ||
|
||
``` | ||
-10 | ||
/ \ | ||
9 20 | ||
/ \ | ||
15 7 | ||
``` | ||
|
||
叶节点 *9*、*15*、*7* 的最大贡献值分别为 *9*、*15*、*7*。 | ||
|
||
得到叶节点的最大贡献值之后,再计算非叶节点的最大贡献值。节点 *20* 的最大贡献值等于 ![20+\max(15,7)=35 ](./p__20+max_15,7_=35_.png) ,节点 *-10* 的最大贡献值等于 ![-10+\max(9,35)=25 ](./p__-10+max_9,35_=25_.png) 。 | ||
|
||
上述计算过程是递归的过程,因此,对根节点调用函数 `maxGain`,即可得到每个节点的最大贡献值。 | ||
|
||
根据函数 `maxGain` 得到每个节点的最大贡献值之后,如何得到二叉树的最大路径和?对于二叉树中的一个节点,该节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值,如果子节点的最大贡献值为正,则计入该节点的最大路径和,否则不计入该节点的最大路径和。维护一个全局变量 `maxSum` 存储最大路径和,在递归过程中更新 `maxSum` 的值,最后得到的 `maxSum` 的值即为二叉树中的最大路径和。 | ||
|
||
![fig1](https://assets.leetcode-cn.com/solution-static/124/1.PNG) ![fig2](https://assets.leetcode-cn.com/solution-static/124/2.PNG) ![fig3](https://assets.leetcode-cn.com/solution-static/124/3.PNG) ![fig4](https://assets.leetcode-cn.com/solution-static/124/4.PNG) ![fig5](https://assets.leetcode-cn.com/solution-static/124/5.PNG) ![fig6](https://assets.leetcode-cn.com/solution-static/124/6.PNG) ![fig7](https://assets.leetcode-cn.com/solution-static/124/7.PNG) | ||
|
||
```Java [sol1-Java] | ||
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); | ||
} | ||
} | ||
``` | ||
|
||
```Python [sol1-Python3] | ||
class Solution: | ||
def __init__(self): | ||
self.maxSum = float("-inf") | ||
|
||
def maxPathSum(self, root: TreeNode) -> int: | ||
def maxGain(node): | ||
if not node: | ||
return 0 | ||
|
||
# 递归计算左右子节点的最大贡献值 | ||
# 只有在最大贡献值大于 0 时,才会选取对应子节点 | ||
leftGain = max(maxGain(node.left), 0) | ||
rightGain = max(maxGain(node.right), 0) | ||
|
||
# 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值 | ||
priceNewpath = node.val + leftGain + rightGain | ||
|
||
# 更新答案 | ||
self.maxSum = max(self.maxSum, priceNewpath) | ||
|
||
# 返回节点的最大贡献值 | ||
return node.val + max(leftGain, rightGain) | ||
|
||
maxGain(root) | ||
return self.maxSum | ||
``` | ||
|
||
```C++ [sol1-C++] | ||
class Solution { | ||
private: | ||
int maxSum = INT_MIN; | ||
|
||
public: | ||
int maxGain(TreeNode* node) { | ||
if (node == nullptr) { | ||
return 0; | ||
} | ||
|
||
// 递归计算左右子节点的最大贡献值 | ||
// 只有在最大贡献值大于 0 时,才会选取对应子节点 | ||
int leftGain = max(maxGain(node->left), 0); | ||
int rightGain = max(maxGain(node->right), 0); | ||
|
||
// 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值 | ||
int priceNewpath = node->val + leftGain + rightGain; | ||
|
||
// 更新答案 | ||
maxSum = max(maxSum, priceNewpath); | ||
|
||
// 返回节点的最大贡献值 | ||
return node->val + max(leftGain, rightGain); | ||
} | ||
|
||
int maxPathSum(TreeNode* root) { | ||
maxGain(root); | ||
return maxSum; | ||
} | ||
}; | ||
``` | ||
```golang [sol1-Golang] | ||
func maxPathSum(root *TreeNode) int { | ||
maxSum := math.MinInt32 | ||
var maxGain func(*TreeNode) int | ||
maxGain = func(node *TreeNode) int { | ||
if node == nil { | ||
return 0 | ||
} | ||
// 递归计算左右子节点的最大贡献值 | ||
// 只有在最大贡献值大于 0 时,才会选取对应子节点 | ||
leftGain := max(maxGain(node.Left), 0) | ||
rightGain := max(maxGain(node.Right), 0) | ||
// 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值 | ||
priceNewPath := node.Val + leftGain + rightGain | ||
// 更新答案 | ||
maxSum = max(maxSum, priceNewPath) | ||
// 返回节点的最大贡献值 | ||
return node.Val + max(leftGain, rightGain) | ||
} | ||
maxGain(root) | ||
return maxSum | ||
} | ||
func max(x, y int) int { | ||
if x > y { | ||
return x | ||
} | ||
return y | ||
} | ||
``` | ||
|
||
```csharp [sol1-C#] | ||
public class Solution | ||
{ | ||
int maxSum = int.MinValue; | ||
|
||
public int MaxPathSum(TreeNode root) | ||
{ | ||
MaxGain(root); | ||
return maxSum; | ||
} | ||
|
||
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); | ||
} | ||
} | ||
``` | ||
|
||
**复杂度分析** | ||
|
||
* 时间复杂度:*O(N)*,其中 *N* 是二叉树中的节点个数。对每个节点访问不超过 *2* 次。 | ||
|
||
* 空间复杂度:*O(N)*,其中 *N* 是二叉树中的节点个数。空间复杂度主要取决于递归调用层数,最大层数等于二叉树的高度,最坏情况下,二叉树的高度等于二叉树中的节点个数。 |
Binary file added
BIN
+1.35 KB
.../src/main/java/com/jacobs/basic/leetcode/editor/cn/doc/p__-10+max_9,35_=25_.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+1.29 KB
...g/src/main/java/com/jacobs/basic/leetcode/editor/cn/doc/p__20+max_15,7_=35_.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion
2
basic-learning/src/main/java/com/jacobs/basic/leetcode/editor/cn/translation.json
Large diffs are not rendered by default.
Oops, something went wrong.