题目描述

✅ 面试题 04.08. 首个共同祖先

image-20260929004923165

题意分析

在普通二叉树中,找出同时包含 p、q 且位置最深的祖先节点,答案可以就是其中一个目标。题目保证两个目标不同且都存在,并要求不额外建立保存其他节点的数据结构,不能把它当作二叉搜索树按值寻找。

解法:后序合并两侧目标信息

核心思路

[!blue]

两个目标若分布在当前节点的左右两侧,它们的路径就在这里汇合;若都在一侧,答案还应继续留在那一侧。用后序递归汇总两边找到的信息,就能定位最低的汇合处,不必建立父指针表或保存两条祖先路径。

递归返回空,表示这棵子树没有目标;只找到一个目标时返回该目标;两个目标都在子树内时,返回它们已经找到的最近公共祖先。父层只需判断两侧是否为空,并保留已有的答案节点。

当前节点就是 p 或 q 时可以直接返回:若另一目标在其下面,当前节点自己就是最近公共祖先;若另一目标在外面,返回当前目标即可让上层继续汇合。两个目标都存在的前提保证最终不需要另做存在性检查。

当前节点不是目标时,递归左右子树。若两侧都非空,两个目标分别在两边,当前节点是它们最深的共同祖先,因为任何更低的子树都只能覆盖一侧。若只有一侧非空,直接上传那一侧的结果;它可能还是单个目标,也可能已经是更深的完整答案,不能擅自换成当前节点。两侧都空时自然返回空。

解题步骤

  1. 空节点或目标节点直接返回。
  2. 递归查询左右子树。
  3. 一侧为空则返回另一侧,两侧非空返回当前根。
  4. 返回根调用得到的节点引用,不按节点值另外查找。

代码实现

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 撞到目标即上报,不再深入。
        if (root == null || root == p || root == q) {
            return root;
        }

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        // 两侧都有:当前节点即分叉点;只有一侧:把该侧结果上传。
        return left == null ? right : (right == null ? left : root);
    }
}
func lowestCommonAncestor(root *TreeNode, p *TreeNode, q *TreeNode) *TreeNode {
    // 撞到目标即上报,不再深入。
    if root == nil || root == p || root == q {
        return root
    }

    left := lowestCommonAncestor(root.Left, p, q)
    right := lowestCommonAncestor(root.Right, p, q)
    // 两侧都有:当前节点即分叉点;只有一侧:把该侧结果上传。
    if left == nil {
        return right
    }
    if right == nil {
        return left
    }
    return root
}

复杂度分析

  • 时间复杂度:$O(n)$,每个被访问的节点只处理一次。
  • 空间复杂度:$O(h)$,只使用递归栈,没有额外的节点集合或父指针表。

关键点总结

[!green]

两侧非空才在当前节点汇合,单侧非空则保留该侧已经找到的更低答案;这使最终返回的是最近公共祖先,而不是任意共同祖先。

易错点总结

[!yellow]

  • 必须比较节点引用,不能把普通树当 BST 按值剪枝。
  • 某目标是另一个目标的祖先时应返回该目标。
  • 单侧为空不表示搜索失败,两个目标可能都在另一侧,应继续上传该侧结果。

相似题目

题目 难度 关联与区别
235. 二叉搜索树的最近公共祖先 中等 原题是 BST,可按值决定下降方向;本题普通树需要查询左右子树。
1123. 最深叶节点的最近公共祖先 中等 同样后序向上合并祖先信息,原题目标集合是所有最深叶子。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/39123335
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!