面试知识库

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

从生活场景理解LCA#

想象你在整理家族族谱时,需要找到两位家族成员的最近共同祖先。比如小明和小红,他们的最近共同祖父可能不是年龄最大的祖先,而是离他们最近的共同长辈。在计算机科学中,二叉树中的最近公共祖先(LCA)问题与此如出一辙。

问题描述#

题目目标#

给定一个二叉树,找到该树中两个指定节点 p 和 q 的最近公共祖先。最近公共祖先的定义为:对于有根树 T 的两个节点 p、q,最近公共祖先表示一个节点 x,满足 x 是 p、q 的祖先,且 x 的深度尽可能大。

示例 1#

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

     3 
 /     \
 5     1
/  \  / \
6  2  0 8
  / \
  7 4
text

说明: 节点 5 和节点 1 分别位于根节点 3 的左右子树中,所以它们的最近公共祖先是 3。

补充说明#

  • 二叉树通常使用层序数组表示,null 表示该位置没有节点。

问题解析#

LeetCode第236题要求:给定一个二叉树和两个节点p、q,找到这两个节点在树中的最近公共祖先。注意:

  1. 节点可以是自身的祖先
  2. p和q都存在于二叉树中
  3. 所有节点值唯一

递归法:优雅的分治策略#

递归思路分解#

我们可以把问题分解为三个层次来思考:

  1. 当前节点是否是p或q?
  2. 目标节点是否在左子树?
  3. 目标节点是否在右子树?

当出现以下情况之一时,当前节点就是LCA:

  • p和q分别位于左右子树(即左右递归都返回非空)
  • 当前节点是p/q,且另一个节点在其子树中

递归过程图解#

以二叉树[3,5,1,6,2,0,8,null,null,7,4]为例,寻找节点5和1的LCA:

        3
      /   \
     5     1   ← LCA是3
    / \   / \
   6  2 0   8
     / \
    7  4
plaintext

递归执行过程:

  1. 根节点3不是目标节点,递归左右子树
  2. 左子树5发现目标节点,右子树1发现另一个目标节点
  3. 根据判断条件,返回根节点3作为LCA

迭代法:显式记录访问路径#

虽然递归法简洁优雅,但可能有读者更习惯迭代思维。我们可以通过记录访问路径的方式实现:

实现步骤#

  1. 使用哈希表存储每个节点的父指针
  2. 记录从p到根节点的访问路径
  3. 从q出发向上查找,第一个出现在p路径中的节点即为LCA

方法对比#

方法时间复杂度空间复杂度实现难度
递归法O(n)O(h)★★☆☆☆
迭代法O(n)O(n)★★★☆☆

递归法凭借其简洁性成为首选方案,迭代法则在特定场景下(如需要避免递归栈溢出)更具优势。

关键点解析#

  1. 后序遍历特性:递归法本质上是后序遍历的应用,先处理子树再处理当前节点
  2. 短路优化:当找到目标节点时,可以提前终止不必要的搜索
  3. 边界处理:巧妙利用空值传递信息,空值表示子树中无目标节点

常见误区#

  1. 误认为LCA必须是父节点(可能是祖父节点)
  2. 忘记处理节点自身是祖先的情况
  3. 混淆节点值和节点引用(Java中要比较对象地址)

实际应用#

  1. Git版本控制中的最近共同提交
  2. 社交网络中的共同联系人查找
  3. 生物信息学中的进化树分析
  4. 编译器中的符号解析

进阶思考#

  1. 如果树节点包含父指针,如何优化算法?
  2. 如何处理多个目标节点的LCA问题?
  3. 当树结构动态变化时,如何高效维护LCA信息?
  4. 在N叉树中如何寻找LCA?

总结#

LCA问题教会我们:

  1. 递归思维在树结构问题中的强大威力
  2. 通过分治策略将复杂问题拆解为子问题
  3. 不同数据结构组合带来的性能优化空间
  4. 算法设计中对边界条件的敏感性

无论是递归法还是迭代法,其核心都在于理解树结构的遍历特性。就像在家族中寻找共同祖先一样,算法本质上是在建立节点间的联系网络。


  1. 高频面试题分类解析
  2. 递归与非递归模板代码
  3. 经典题目变形题解
  4. 大厂真题实战演练

让我们一起在算法的世界里修炼忍者之道!🗡️