Leetcode 二叉树 7

题号直达: 236 二叉树的最近公共祖先 | 124 二叉树中的最大路径和

LeetCode 236. 二叉树的最近公共祖先 ↑

题目

给定一个二叉树,找到该树中两个指定结点 p、q 的最近公共祖先。

根据百度百科中「最近公共祖先」的定义:对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个结点也可以是它自己的祖先)。

提示

  • 树中结点数在范围 [2, 10^5] 内
  • -10^9 ≤ Node.val ≤ 10^9,且所有结点值互不相同
  • p != q,且 p、q 均存在于给定二叉树中

代码(Python)解法一:后序 DFS + 标记是否命中 p / q

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def __init__(self):
        self.ans = None

    def dfs(self, root, p, q):
        if not root:
            return False
        lson = self.dfs(root.left, p, q)
        rson = self.dfs(root.right, p, q)
        if (lson and rson) or (
            (root.val == p.val or root.val == q.val) and (lson or rson)
        ):
            self.ans = root
        return lson or rson or root.val == p.val or root.val == q.val

    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        self.dfs(root, p, q)
        return self.ans

视频讲解(解法一)

思路解析(解法一)

  • 返回值语义:dfs 返回当前子树中是否包含 p 或 q(或二者之一),用布尔值向上传递。
  • lson / rson:左、右子树是否「已命中」p 或 q。
  • 更新答案:若左右子树各命中一个(lson and rson),则当前 root 必为 LCA;若当前根本身是 p 或 q 之一,且某一侧子树已含另一个,则 LCA 亦为当前根(含「结点可为自身祖先」)。
  • self.ans:初值为 None;首次满足上述条件时赋值(题面保证存在唯一合理 LCA,最后一次写入即为答案)。
  • 叶上推:return lson or rson or root.val == p.val or root.val == q.val 表示本子树是否覆盖 p/q 之一;值互不相同故可用 val 比较。
  • 复杂度:每个结点访问一次,时间 O(n),递归栈 O(h),h 为树高。

代码(Python)解法二:BFS 建「父结点」映射 + 从 q 向上找

先用层序遍历得到 record:子结点 -> 父结点,并令 record[root] = None。
再从 p 一直沿父指针走到根,得到 p 的祖先链(含 p 自身)存入 father_path。
最后从 q 沿父指针向上走,第一次出现在 father_path 中的结点即为 LCA(最深公共祖先)。

from collections import deque

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        record = {}
        queue = deque()
        queue.append(root)
        while queue:
            temp = queue.popleft()
            if temp.left:
                queue.append(temp.left)
                record[temp.left] = temp
            if temp.right:
                queue.append(temp.right)
                record[temp.right] = temp
        record[root] = None

        father_path = []
        temp_p = p
        while temp_p in record:
            father_path.append(temp_p)
            temp_p = record[temp_p]

        temp_q = q
        father_path.append(root)
        while temp_q in record:
            if temp_q in father_path:
                return temp_q
            temp_q = record[temp_q]

思路解析(解法二)

  • record:除根外,每个结点记录父结点;record[root] = None 表示向上到头。
  • father_path:从 p 沿父指针走到 None 前,依次加入 p 及其所有祖先(含根)。father_path.append(root) 在从 q 开始走之前执行:若 p 到根的路径里已含 root,此处会重复一次 root,一般不影响 in 判断。
  • 为何从 q 往上找:从 q 向根方向,由深到浅经过 q 的祖先;第一个落在「p 的祖先集合」里的结点,就是最深的公共祖先。
  • 与解法一:无递归栈,但需要 O(n) 哈希存父指针、O(n) 祖先列表;时间仍为 O(n)。

LeetCode 124. 二叉树中的最大路径和 ↑

题目

二叉树中,路径被定义为:结点序列中相邻结点在树中有边相连;每个结点在路径中至多出现一次;路径至少包含一个结点,且不必经过根。

路径和为路径上各结点值之和。给定二叉树的根 root,返回最大路径和。

提示

  • 树中结点数在范围 [1, 3 × 10^4] 内
  • -1000 ≤ Node.val ≤ 1000

代码(Python)

maxGain(node):从 node 出发、沿某一子方向向下延伸的一条链的最大和(子树贡献与 0 取较大,避免拖累负和)。
对每个结点 temp,以「经过 temp」的折线路径和为:temp.val + max(left,0) + max(right,0),再 BFS 枚举所有结点取全局最大。ans 初值取小于 -1000(如 -1001),以覆盖全负结点情形。

from collections import deque

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def maxGain(self, root):
        if not root:
            return 0
        if not root.right and not root.left:
            return root.val
        return (
            max(self.maxGain(root.left), self.maxGain(root.right), 0) + root.val
        )

    def maxPathSum(self, root):
        """
        :type root: Optional[TreeNode]
        :rtype: int
        """
        if not root:
            return 0
        stack = deque()
        stack.append(root)
        ans = -1001
        while stack:
            temp = stack.popleft()
            value = (
                temp.val
                + max(self.maxGain(temp.left), 0)
                + max(self.maxGain(temp.right), 0)
            )
            if value > ans:
                ans = value
            if temp.left:
                stack.append(temp.left)
            if temp.right:
                stack.append(temp.right)
        return ans

视频讲解

思路解析

  • 经过某结点的路径:只能在该结点左、右各接一条向下的链,因此用 maxGain 左子树与 maxGain 右子树,与 0 取 max 以丢弃负贡献。
  • maxGain 叶结点:无子结点时直接返回 root.val。
  • 全局最大:对每个结点算一条「倒 V」路径和,BFS 取最大。
    注意:对每个结点重复调用 maxGain 会子树重复计算,最坏 O(n²),在 n 较大时可能超时;若需 O(n),可改为一次后序遍历在递归中同时维护全局 ans(本笔记暂记当前写法,仅作存档)。
  • 初值 ans = -1001:保证全树结点值均为负时,答案仍取单个最大结点而非错误地取 0。
posted @ 2026-10-12 00:08  Phier  阅读(5)  评论(0)    收藏  举报