Skip to content

Commit

Permalink
max distance.
Browse files Browse the repository at this point in the history
  • Loading branch information
lichaojacobs committed Jan 25, 2022
1 parent c8df410 commit c65481d
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 2 deletions.
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)

}

Large diffs are not rendered by default.

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* 是二叉树中的节点个数。空间复杂度主要取决于递归调用层数,最大层数等于二叉树的高度,最坏情况下,二叉树的高度等于二叉树中的节点个数。
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Large diffs are not rendered by default.

0 comments on commit c65481d

Please sign in to comment.