一、题目深度解析与核心定义
题目描述
在二叉树中找到两个节点p和q的最近公共祖先(LCA)。最近公共祖先是指两个节点的所有祖先中距离它们最近的那个节点。二叉树的节点可以包含任意值,且不一定是搜索树,因此无法利用值的大小关系,只能通过树的结构遍历求解。
核心性质
- 递归定义:对于当前节点
root,若root是p或q,或者p和q分别在root的左右子树中,则root是LCA。 - 后序遍历特性:从底向上查找,先处理子树再处理当前节点,便于判断子树中是否存在目标节点。
示例说明
输入二叉树:
3
/ \
5 1
/ \ / \
6 2 0 8
/ \
7 4
p=5,q=4
输出:5
LCA是5,因为5是包含p和q的最底层祖先。
二、递归解法的核心实现与逻辑框架
完整递归代码实现
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 终止条件:当前节点为空,或当前节点是p或q
if (root == null || root == p || root == q) {
return root;
}
// 递归查找左子树和右子树
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 根据左右子树的返回结果判断当前节点是否为LCA
if (left == null && right == null) { // 左右子树都没找到,返回null
return null;
} else if (left != null && right == null) { // 只有左子树找到,返回左子树结果
return left;
} else if (left == null && right != null) { // 只有右子树找到,返回右子树结果
return right;
} else { // 左右子树都找到,说明当前节点是LCA
return root;
}
}
}
核心变量与递归逻辑
-
终止条件:
root == null:空节点,无祖先root == p || root == q:当前节点是其中一个目标节点,向上返回作为候选祖先
-
递归过程:
- 先递归左子树和右子树,获取左右子树中是否存在目标节点或祖先
- 根据左右返回结果,判断当前节点是否为LCA
三、核心问题解析:递归逻辑与祖先判定
1. 递归终止条件的设计
if (root == null || root == p || root == q) {
return root;
}
- 空节点处理:递归到底层节点,返回null作为无效祖先
- 目标节点匹配:当找到p或q时,向上传递该节点,作为祖先查找的起点
2. 后序遍历的祖先判定逻辑
四种返回情况分析:
-
左右都为空(left == null && right == null):
- 左右子树都不存在p或q,当前子树无祖先,返回null
-
左存在右不存在(left != null && right == null):
- 左子树中存在p或q(或其祖先),右子树无,返回左子树结果
-
右存在左不存在(left == null && right != null):
- 右子树中存在p或q(或其祖先),左子树无,返回右子树结果
-
左右都存在(left != null && right != null):
- p和q分别在左右子树中,当前节点是最近公共祖先,返回root
逻辑本质:
- 后序遍历保证从叶子节点开始向上判断,当左右子树都能找到目标节点时,当前节点必然是它们的最近公共祖先
四、算法复杂度分析
1. 时间复杂度
- O(n):每个节点最多访问一次,n为树的节点数
- 递归过程中每个节点执行常数级判断,总时间线性于节点数
2. 空间复杂度
- O(h):h为树的高度(递归栈深度)
- 平衡树:h=logn,空间复杂度O(logn)
- 最坏情况(链表树):h=n,空间复杂度O(n)
五、核心技术点总结:递归解法的三大关键
1. 后序遍历的逆向追踪
- 自底向上:从叶子节点开始判断,逐步向上汇聚结果
- 路径压缩:无需记录路径,通过递归返回值直接确定祖先
2. 终止条件的双重作用
- 空节点过滤:避免无效递归路径
- 目标节点捕获:及时返回目标节点,作为祖先查找的起点
3. 结果合并逻辑
- 左右子树结果分析:
- 仅左存在:祖先在左子树
- 仅右存在:祖先在右子树
- 左右都存在:当前节点是LCA
六、常见误区与边界处理
1. 错误理解公共祖先定义
- 误区:认为祖先必须同时是p和q的父节点
- 正确逻辑:祖先可以是p或q本身(当其中一个节点是另一个的祖先时)
2. 忽略空树处理
- 边界条件:当root为空时返回null,代码中已通过终止条件处理
3. 优化建议:记忆化搜索
- 场景:多次查询LCA时可缓存结果
- 实现:使用哈希表存储节点的父节点,逐层向上查找
- 对比:递归解法更简洁,适合单次查询
七、总结:递归法求解LCA的本质是后序汇聚
本算法通过递归后序遍历,利用树的层次结构特性,从底向上追踪目标节点,最终在最近公共祖先处汇聚左右子树的结果。核心在于:
- 递归终止条件:准确捕获目标节点和空节点,作为递归的起点和边界
- 后序处理逻辑:先处理子树再处理当前节点,便于判断祖先关系
- 结果合并策略:根据左右子树的返回值,高效判定当前节点是否为LCA
这种递归解法简洁且高效,充分利用了树的递归结构特性,无需额外数据结构,是求解二叉树LCA问题的经典方法。理解其核心逻辑后,可轻松扩展到二叉搜索树等变种问题,体现了递归在树结构问题中的强大表现力。
469

被折叠的 条评论
为什么被折叠?



