1. 题目
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
2. 示例
3. 分析
方法一:1.对于二叉树,从根开始搜索,查找两个子节点的位置
(1)都在左子树,递归到左子树去找
(2)都在右子树,递归到右子树去找
(3)一个节点在左子树,一个节点在右子树,那么根就是最近公共祖先
2.找节点时,用指针去找,不能用值去找,因为有可能二叉树中存在值相等的节点
/**
* 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 IsIntree(TreeNode* root, TreeNode* x)
{
if (root == nullptr)
return false;
if (root == x)
return true;
return IsIntree(root->left, x) || IsIntree(root->right, x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr)
return nullptr;
// p或q是根,另一个是孩子,root就是最近公共祖先
if (p == root || q == root)
return root;
// 题目已经说明p和q一定在树中
// 如果p不在左子树,那么p一定在右子树
// 不需要在左右子树中都查找p,只需要在左子树中查找p
// q也一样
bool pInLeft = IsIntree(root->left, p);
bool pInRight = !pInLeft;
bool qInLeft = IsIntree(root->left, q);
bool qInRight = !qInLeft;
// 1、一个在我的左树,一个在我的右树,我就是最近公共祖先
// 2、都在左,就换成子问题,在左子树去找公共祖先
// 3、都在右,就换成子问题,在右子树去找公共祖先
if ((pInLeft && qInRight) || (qInLeft && pInRight))
return root;
else if (pInLeft && qInLeft)
return lowestCommonAncestor(root->left, p, q);
else
return lowestCommonAncestor(root->right, p, q);
}
};
由于方法一在极端情况下复杂度为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)确定长栈和短栈,让长栈先走差距步,然后长短栈一起走,找相同的栈顶元素
class Solution {
public:
// 判断节点路径是否存在
bool GetPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
{
if (root == nullptr)
return false;
path.push(root);// 路径节点入栈
if (root == x)
return true;
// 到左子树寻找
if (GetPath(root->left, x, path))
return true;
// 到右子树寻找
if (GetPath(root->right, x, path))
return true;
path.pop();// 不是路径节点就给删掉
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
stack<TreeNode*> pPath, qPath;
GetPath(root, p, pPath); // 获得p路径
GetPath(root, q, qPath); // 获得q路径
// 路径长的先出
while (pPath.size() != qPath.size())
{
if (pPath.size() > qPath.size())
pPath.pop();
else
qPath.pop();
}
// 节点一样就相交了,也就是最近公共祖先了
while (pPath.top() != qPath.top())
{
pPath.pop();
qPath.pop();
}
return pPath.top();
}
};