在问 · 算法题解

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

中等二叉树后序递归力扣原题 ↗

题目描述

输入一棵普通二叉树的根节点 root,以及树中两个不同的节点 p 和 q。树用层序数组表示,空位记为 null;p、q 用节点值给出。

若节点 x 同时是 p 和 q 的祖先,它就是二者的公共祖先。节点本身也算自己的祖先。所有公共祖先里深度最大的一个,叫做最近公共祖先。

返回 p 和 q 的最近公共祖先。

示例 1
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3

解释:5 和 1 分别是根的左右孩子,最近公共祖先是根节点 3。

示例 2
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5

解释:4 在节点 5 的子树里,5 自身也算祖先,所以答案是 5。

示例 3
输入:root = [1,2], p = 1, q = 2
输出:1

解释:2 是 1 的孩子,最近公共祖先是 1。

数据范围

  • 节点个数在 [2, 10^5] 之间。
  • -10^9 <= Node.val <= 10^9
  • 所有 Node.val 两两不同。
  • p != q
  • p 和 q 都在这棵树中。

解题思路:后序递归

后序递归:遇到 p 或 q 直接返回;左右都返回非空说明当前节点是分叉点,否则向上传递唯一非空结果。

为什么正确

递归返回子树中找到的目标节点,或已经确定的最近公共祖先。

复杂度分析

时间 O(n),递归栈 O(h);h 为树高,最坏可为 n;Python 递归受解释器递归深度限制,深链树应改用显式栈。

易错点

比较节点身份而非值;当某个目标本身是另一个目标的祖先时,应直接返回该目标。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def lowestCommonAncestor(self, root, p, q):
        if root is None or root is p or root is q:
            return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if left and right:
            return root
        return left or right

载入编辑器,自己跑一遍 →

其他解法

后序递归代码短,空间只有递归栈 O(h);记录父节点的做法用 O(n) 空间把问题变成链表交点,并且可以完全迭代。

解法 2:记录父节点 时间 O(n),空间 O(n)

用显式栈遍历整棵树,把每个节点的父节点记入哈希表。从 p 沿父指针一直走到根,把经过的节点放入集合。再从 q 沿父指针向上走,第一个在集合中的节点就是答案。

不用递归,深链树不会栈溢出。需要哈希表和集合,空间 O(n)。把问题转成“两条向上的链表求交点”,便于理解。

class Solution:
    def lowestCommonAncestor(self, root, p, q):
        parent = {root: None}
        stack = [root]
        while stack:
            node = stack.pop()
            for child in (node.left, node.right):
                if child:
                    parent[child] = node
                    stack.append(child)
        ancestors = set()
        while p:
            ancestors.add(p)
            p = parent[p]
        while q not in ancestors:
            q = parent[q]
        return q

二叉树 专题的其他题目

查看 二叉树 全部题目 →