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。

浙公网安备 33010602011771号