解法1:
解法1借鉴于力扣官方解法,这里只是记录一下
二叉树的最近公共祖先一共有三种情况:
1. p或者q在左子树
2. p或者q在右子树
3. 根自己就是p或者q,则另一个p或q在左子树中或是右子树中
记左子树为, 则右子树为
,上述情况可表达为:
根据上面表达式,可以确定公共祖先。
当左子树包含p或q时,左子树为真,当右子树包含p或q时,右子树为真,根为p或q时,根为真,根据此,可以递归向下找,向下的条件为:
可以写出以下代码
/**
* 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* ans;
bool dfs(TreeNode* root, p, q)
{
if(root == NULL)
return false;
bool left = dfs(root->left,p,q);
bool right = dfs(root->right,p,q);
if((left&&right)||((root->val == p||root->val == q)&&(left||right))
ans = root;
return (left||right||(root->val == p || root-val == q);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root,p,q);
return ans;
}
};
解法二:
通过p和q的路径去找,两种情况
1. 当p和q不在同一路径上,p和q在左子树或是右子树里。
2. 当p和q在同一路径上时,p和q有一个为根节点。
第一种情况,找到p的路径和q的路径,找到二者路径不同前的最后一个相同的节点,即为最近公共祖先。
第二种情况,同样找到二者的路径,p和q最后一个相同的节点即为最近公共祖先。
这里可以借用栈来实现。
解法三:
利用递归的解法。
首先明确基本情况是
1. 当递归到为空时,肯定返回空。
2. 当找到了p或者q时,直接返回这个节点。
可以写出基本的代码,也就是函数的出口。
递归去返回左边和右边的情况,这里用后序遍历的时机,也就是说当子树已经知道自己是什么情况的时候,往上逐层去返回。
分别从左边去递归和从右边去递归,左边要不然找到p或者q,要不然为空,右边也是一样。
每个子树知道了自己的情况后,该怎么向上面汇报呢,或者说汇报什么呢,左右子树又有四种情况:
1. 左右子树都为空,自然返回空;
2. 左右子树都不为空,找到了!返回root;
3. 左边不为空右边为空,返回左边的;
4. 右边不为空左边为空,返回右边的;
最后两种情况可以综合一下,哪边不为空返回哪边的。
所以可以写出如下的代码:
/**
* 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) {
if(root == NULL)
return root;
if(root == q || root == p)
return root;
TreeNode* ltree = lowestCommonAncestor(root->left, p,q);
TreeNode* rtree = lowestCommonAncestor(root->right,p,q);
if(ltree == NULL && rtree == NULL)
return NULL;
if(ltree!= NULL && rtree!= NULL)
return root;
//if(ltree == NULL || rtree == NULL)
return ltree == NULL ? rtree : ltree;
}
};