题目:236. 二叉树的最近公共祖先
代码实现
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// 如果根节点是 p 或 q,或根节点为空,直接返回根节点
if (root == q || root == p || root == NULL) return root;
// 递归搜索左子树
TreeNode* left = lowestCommonAncestor(root->left, p, q);
// 递归搜索右子树
TreeNode* right = lowestCommonAncestor(root->right, p, q);
// 如果左子树和右子树都找到了 p 或 q,说明 root 是公共祖先
if (left != NULL && right != NULL) return root;
// 如果左子树为空,右子树非空,返回右子树
if (left == NULL && right != NULL) return right;
// 如果右子树为空,左子树非空,返回左子树
else if (left != NULL && right == NULL) return left;
// 如果左右子树都为空,返回 NULL
else return NULL;
}
};
执行流程
示例输入
root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
树结构
3
/ \
5 1
/ \ / \
6 2 0 8
/ \
7 4
步骤
- 递归搜索左子树:
lowestCommonAncestor(5, 5, 1)
- 找到
p = 5
,返回5
- 找到
- 递归搜索右子树:
lowestCommonAncestor(1, 5, 1)
- 找到
q = 1
,返回1
- 找到
- 左右子树都返回了非空结果,说明公共祖先是
3
,即返回root = 3
最终输出:
3
关键思路
- 递归树遍历
- 从根节点开始,递归向左右子树遍历。
- 若当前节点是
p
或q
,则返回该节点(可能为p
或q
,也可能为NULL
)。
- 判断当前节点是否是公共祖先
- 如果 左右子树 都找到
p
或q
,当前节点root
就是它们的 公共祖先。 - 如果 左子树 返回
NULL
,说明p
和q
都不在左子树上,返回右子树的结果(反之亦然)。
- 如果 左右子树 都找到
- 递归终止条件
- 当前节点为空,或者当前节点就是
p
或q
时返回当前节点。
- 当前节点为空,或者当前节点就是
时间 & 空间复杂度
操作 | 时间复杂度 | 空间复杂度 |
---|---|---|
遍历树的每个节点 | O(n) | O(h) |
- 时间复杂度: 最坏情况下需要遍历树的每个节点,因此时间复杂度是 O(n),其中 n 是树的节点数。
- 空间复杂度: 空间复杂度主要由递归栈决定,最坏情况下为树的高度 h,即 O(h)。对于平衡树,空间复杂度为 O(logn),对于极端情况的树(如链状树),空间复杂度为 O(n)。
基础语法解析
1. if (root == q || root == p || root == NULL)
- 递归的终止条件: 如果当前节点是
p
或q
,或者当前节点为空,返回当前节点。
if (root == q || root == p || root == NULL) return root;
2. TreeNode\* left = lowestCommonAncestor(root->left, p, q);
- 递归向左子树查找公共祖先。
TreeNode* left = lowestCommonAncestor(root->left, p, q);
3. if (left != NULL && right != NULL)
- 如果左右子树都找到了
p
或q
,则当前节点root
是最近公共祖先。
if (left != NULL && right != NULL) return root;
4. return left == NULL ? right : left;
- 如果左子树为空,返回右子树的结果;反之,返回左子树的结果。
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
优化 & 变种
变种 1:判断一个节点是否存在于树中
在某些情况下,我们可能需要判断 p
或 q
是否在树中。此时,可以修改 lowestCommonAncestor
函数,添加存在性判断:
bool exists(TreeNode* root, TreeNode* target) {
if (!root) return false;
if (root == target) return true;
return exists(root->left, target) || exists(root->right, target);
}
这个辅助函数用于判断 p
或 q
是否存在于树中。
总结
C++ 语法/结构 | 作用 |
---|---|
`if (root == p | |
left != NULL && right != NULL | 判断左右子树都找到目标,当前节点为公共祖先 |
return left == NULL ? right : left; | 如果左子树为空,返回右子树的结果,反之亦然 |
时间复杂度: O(n) | 遍历所有节点 |
空间复杂度: O(h) | 递归栈空间 |