二叉树 6 (二叉树中的最大路径和 leetcode 124)

105 阅读3分钟

思想

二叉树的核心思想是分治和递归,特点是遍历方式。
解题方式常见两类思路:

  1. 遍历一遍二叉树寻找答案;
  2. 通过分治分解问题寻求答案;

遍历分为前中后序,本质上是遍历二叉树过程中处理每个节点的三个特殊时间点:

  1. 前序是在刚刚进入二叉树节点时执行;
  2. 后序是在将要离开二叉树节点时执行;
  3. 中序是左子树遍历完进入右子树前执行;
# 前序
     1 node
    /      \
 2 left   3 right
中左右
 
# 中序
     2 node
    /      \
 1 left    3 right
左中右
 
# 后序
     3 node
    /      \
 1 left    2 right     
左右中       

多叉树只有前后序列遍历,因为只有二叉树有唯一一次中间节点的遍历

题目的关键就是找到遍历过程中的位置,插入对应代码逻辑实现场景的目的。

实例

二叉树中的最大路径和 leetcode 124

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

输入:
root: TreeNode,二叉树的根节点

输出:
int,返回二叉树中的最大路径和,路径是指从树中任意节点出发,沿父-子节点连接,达到任意节点的序列,且同一个节点在同一路径中至多出现一次。路径和指的是路径中各个节点值的总和。

举例:
给定二叉树 [-10,9,20,null,null,15,7]
路径包括: 9
9 -> -10
9 -> -10 -> 20
9 -> -10 -> 20 -> 7
9 -> -10 -> 20 -> 15
-10
-10 -> 20
-10 -> 20 -> 15
-10 -> 20 -> 7
20
20 -> 15
20 -> 7
15
15 -> 20 -> 7
要注意,最大路径和不一定是最长路径,例如上面的最大路径和是 15 + 20 + 7 = 42。

二叉树的数据存储可以使用链表,也可以使用数组,往往数组更容易表达,根节点从 index=1 处开始存储,浪费 index=0 的位置
left_child = 2 * parent
right_child = 2 * parent + 1 parent = child // 2

  -10
  / \
 9  20
    / \
   15   7

分治解

从根节点开始,先简化问题,有三种路径形成方式:

  1. 左右子树的最大路径和都小于 0,这时只取根节点,是最大路径和;
  2. 左子树的最大路径和小于 0,此时最大路径和的路径是:根节点 + 右子树的最大路径和所经过的路径;
  3. 右子树的最大路径和小于 0,此时最大路径和的路径是:根节点 + 左子树的最大路径和所经过的路径;

上例来看:

  • 从根节点 -10 出发,左子树最大路径和是 9,右子树是 20 + 15 = 35,当下最大路径和是 -10 + 9 + 35 = 34;
  • -10.left=9,从 9 出发,左右子树都是空,所以当下最大路径和是 9;
  • -10.right=20,从 20 出发,左子树最大路径和是 15,右子树最大路径和是 7,当下最大路径和是 15 + 20 + 7 = 42;
  • 20.left=15,从 15 出发,左右子树都是空,所以当下最大路径和是 15;
  • 20.right=7,从 7 出发,左右子树都是空,所以当下最大路径和是 7;

全局最大路径和是 42

编码


from typing import Optional


class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


def binary_tree_maximum_path_sum(root: Optional[TreeNode]) -> int:
    max_sum  = None

    def max_path_sum(root: Optional[TreeNode]) -> int:
        # 初始条件,空时返回 0
        if root is None:
            return 0
        # 判断左右子树最大值,如果小于 0 则路径不选择左右子树的节点
        left_max = max(0, max_path_sum(root.left))
        right_max = max(0, max_path_sum(root.right))
        # 更新全局最大值
        nonlocal max_sum
        if max_sum is None:
            max_sum = root.val + left_max + right_max
        else:
            max_sum = max(root.val + left_max + right_max, max_sum)
        # 返回当前遍历的最大值,一定包含 root 节点,并选择左右子树较大的一个
        return root.val + max(left_max, right_max)

    max_path_sum(root)
    return max_sum

相关

二叉树 0
二叉树 1
二叉树 2
二叉树 3
二叉树 4
二叉树 5