题目链接
题目描述
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
题目分析
这题显然是要从下往上去看,而我们知道二叉树的遍历是从根节点开始的,但是处理顺序可以从下往上。
- 第一种情况:例如
p=3, q=5
或p=0, q=3
,p
和q
分别存在于最近公共祖先节点的左子树或右子树中。 - 第二种情况:例如
p=2, q=5
或是p=4, q=5
,最近公共祖先节点就是p
或q
。
但无论是哪种情况,我们都要从下往上去找最近公共祖先节点。
第一种情况:
对于第一种情况来说,我们首先要找到p
和q
,然后向上传递,直到某节点的左右子树都包含p
或q
,则这个节点就是他们的最近公共祖先。
- 确定递归参数及返回值:因为我们想将p和q向上传递,所以返回类型仍然是
TreeNode*
。传入参数就是当前节点和p和q:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
- 确定递归终止条件:如果当前节点为空,则返回空;如果当前节点是p或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);
,然后总结每种情况并返回相应的值。这里包含以下四种情况:
(1)如果该节点左右子树分别含有p或q,则当前节点是最近公共祖先,返回当前节点;
if(left!=NULL && right!=NULL) return root;
(2)如果当前节点左子树含有p或q,右子树不含,则返回左子树中含有的节点p或q;
else if(left!=NULL && right==NULL) return left;
(3)如果当前节点右子树含有p或q,左子树不含,则返回右子树中含有的节点p或q;
else if(left==NULL && right!=NULL) return right;
(4)如果左右子树都不包含,则返回空
else return NULL;
整体cpp代码如下:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
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);
// 如果该节点左右子树分别含有p或q,则是最近公共祖先,返回当前节点;
if(left!=NULL && right!=NULL) return root;
// 如果当前节点左子树含有p或q,右子树不含,则返回左子树中含有的节点p或q;
else if(left!=NULL && right==NULL) return left;
// 如果当前节点右子树含有p或q,左子树不含,则返回右子树中含有的节点p或q;
else if(left==NULL && right!=NULL) return right;
// 如果左右子树都不包含,则返回空
else return NULL;
}
};
实际上我们写出的代码就已经包含了上面说的第二种情况。因为函数一开始就要判断当前节点是不是p或q,如果p或q一方本来就是最近公共祖先,那么他最后总会传递到最后。