1. 题目
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
2. 示例
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
3. 分析
方法一:
1.对于二叉树,从根开始搜索,查找两个子节点的位置
(1)都在左子树,递归到左子树去找
(2)都在右子树,递归到右子树去找
(3)一个节点在左子树,一个节点在右子树,那么根就是最近公共祖先
2.找节点时,用指针去找,不能用值去找,因为有可能二叉树中存在值相等的节点
由于方法一在极端情况下复杂度为O(N^2)
因此可以使用下面的方法将时间复杂度优化为O(N)
方法二 借助栈存节点路径,假如要找6和4的最近公共祖先:
(1)从最左开始,找6:
3不是6,有可能是6的路径上的节点,入栈,
5不是6,有可能是6的路径上的节点,入栈
6,找到了,入栈
(2)从最左开始,找4:
3不是4,有可能是4的路径上的节点,入栈
5不是4,有可能是4的路径上的节点,入栈
6不是4,有可能是4的路径上的节点,入栈
6是叶子节点,这条路径上不可能有4,6出栈
2不是4,有可能是4的路径上的节点,入栈
7不是4,有可能是4的路径上的节点,入栈
7是叶子节点,这条路径上不可能有4,7出栈
4找到了,入栈
(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:
bool Find(TreeNode* root,TreeNode* x)
{
if(root == nullptr)
{
return false;
}
if(x == root)
{
return true;
}
return Find(root->left,x)||Find(root->right,x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(p == root || q == root)
{
return root;
}
//题目已经说明p和q一定在树中
bool ispInLeft,ispInRight,isqInLeft,isqInRight;
//如果p不在左子树,那么p一定在右子树
//不需要在左右子树中都查找p,只需要在左子树中查找p
ispInLeft = Find(root->left,p);
printf("ispInLeft = %d\n",ispInLeft);
ispInRight = !ispInLeft;
//如果q不在左子树,那么q一定在右子树
//不需要在左右子树中都查找q,只需要在左子树中查找q
isqInLeft = Find(root->left,q);
printf("isqInLeft = %d\n",isqInLeft);
isqInRight = !isqInLeft;
if(ispInLeft && isqInLeft)//p和q都在左子树中,就去左子树中找
{
return lowestCommonAncestor(root->left,p,q);
}
else if(ispInRight && isqInRight)//p和q都在右子树中,就去右子树中找
{
return lowestCommonAncestor(root->right,p,q);
}
else//一个在左子树,一个在右子树,根就是公共祖先
{
return root;
}
}
};
方法二:
class Solution {
public:
//找节点所在路径
bool FindPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& path)
{
if(root == nullptr)
{
return false;
}
path.push(root);
if(root == x)
{
return true;
}
if(FindPath(root->left,x,path))
{
return true;
}
if(FindPath(root->right,x,path))
{
return true;
}
//左右子树都没有x,那么说明root不是路径中的节点,pop
path.pop();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
stack<TreeNode*> pPath,qPath;
FindPath(root,p,pPath);
FindPath(root,q,qPath);
//确定长栈和短栈
stack<TreeNode*>* longPath = &pPath;
stack<TreeNode*>* shortPath = &qPath;
if(pPath.size() < qPath.size())
{
swap(longPath,shortPath);
}
//让长的先走差距步
while(longPath->size() > shortPath->size())
{
longPath->pop();
}
//同时走,找交点
while(longPath->top() != shortPath->top())
{
longPath->pop();
shortPath->pop();
}
return longPath->top();
}
};