高频算法面试题(三十九)- 二叉树中的最大路径和

307 阅读1分钟

「这是我参与11月更文挑战的第 24 天,活动详情查看:2021最后一次更文挑战

刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能

二叉树中的最大路径和

题目来源LeetCode-124. 二叉树中的最大路径和

题目描述

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次。该路径 至少包含一个 节点,且不一定经过根节点

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和

示例

示例 1

1.png

输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

示例 2

2.png

输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

提示:

  • 树中节点数目范围是 [1, 3 * 10^4]
  • 1000 <= Node.val <= 1000

解题

解法一:递归

思路

从题意中可以知道,二叉树路径就是

  1. 从任意结点出发,达到任意结点
  2. 该路径至少包含一个结点,且不一定经过根节点

比如4 → 2 → 1 → 3;或者6 → 3 → 7;或者路径上只有一个结点2

3.png 可以发现,这个路径和二叉树的区别就是:

  • 二叉树的一个结点,被一个父节点连接,连接左右两个子节点
  • 而路径中,途径一个结点,只能选择来去两个方向(所以,其实就是让我们三选二)

以2这个节点来说,途径2就有下边三种情况:

  1. 左+右
  2. 上+左
  3. 上+右

4.png 假设从底往上出发(从4出发)

  • 左右子节点只能选一个
  • 当走到一个父节点的时候,可以告诉父节点,自己的值是多少,父节点会拿着该子节点的值和另外一个子节点进行比较,选择更大的那个子节点加到自己本身上,然后继续往上走
  • 或者不能继续往上走了,则拐弯连接左右子节点

有了上边的大致思路,应该就有解题思路了

假设有一个二叉树单元

  • a是根节点,与上层的父节点相连(假设有的话)

  • b和c是子节点,与其各自子节点中路径最大值的节点相连

  • 所有可能的路径情况

    • 左中右 b+a+c
    • 左 b + a
    • 右 c + a

5.png 选择左还是右?我们可以定义一个函数,可以计算当前结点通往下方节点的最大路径和

当把a传进去的时候,需要知道b和c通往下方的最大路径和,只需要对b和c进行递归,拿到返回值,取较大的那个,再加上a的值,就得到了a通往下方的最大路径和。需要注意的是,因为整个二叉树的最大路径和,不一定经过根节点,所以,答案并不是根节点的返回值。因此,就需要一个能够记录,所有路径和中最大值的全局变量,当求得某个路径的最大和之后,更新到这个全局最大和中

  • 递归调用b和c
  • 计算b+a和c+a,选择较大的值作为返回值
  • 更新到全局最大和

需要考虑结点值为负数的情况,为了保证和最大,负数应该尽可能的舍弃(可以使用max(0, x))。注意,无论是继续向上还是连接b和c,a作为必经过的地方,是不能舍弃的(防止结点全是负数的情况下,代码就不能获取正确结果了)

代码

//二叉树中的最大路径和
func maxPathSum(root *TreeNode) int {
	maxSum := math.MinInt64 //初始化全局最大和
	var maxValue func(*TreeNode) int
	maxValue = func(node *TreeNode) int { // 用于计算某个节点通往下方的最大路径和
		if node == nil {
			return 0
		}

		leftValue := max(maxValue(node.Left), 0) //去除负数的情况
		rightValue := max(maxValue(node.Right), 0)

		priceNewPath := node.Val + leftValue + rightValue //这两步的目的是为了在全是负数的情况下,不舍弃当前结点本身
		maxSum = max(priceNewPath, maxSum)

		return node.Val + max(leftValue, rightValue) //选左右节点的最大值加上其本身的值
	}
	maxValue(root)

	return maxSum
}

func max(x, y int) int {
	if x > y {
		return x
	}

	return y
}