题目
思路1:
该方法比较巧妙,就是如果p,q在一个结点的两边,那么他就是最近的公共祖先。使用这个思路,就是找这个结点是不是在左右两边。
如果pq都在左边/右边,就再去左/右子树去寻找,直到pq分别在左右子树即可。
class Solution {
public:
// 找x在没在该树下面,在返回true,不再返回false
bool IsInTree(TreeNode* root, TreeNode* x)
{
if(root == nullptr)
{
return false;
}
//注意该返回方式,很巧妙,找到就结束了。
return root == x
|| IsInTree(root->left, x)
|| IsInTree(root->right, x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr)
{
return nullptr;
}
//只要有一个结点是root,该结点必是祖先
if(p == root || q == root)
{
return root; //这是出口1
}
//根不是,这种方法也很巧妙,看是否在左边,不再就在右边。
bool pLeft = IsInTree(root->left,p);
bool pRight = !pLeft;
bool qLeft = IsInTree(root->left, q);
bool qRight = !qLeft;
if((pLeft && qRight) || (pRight && qLeft))
{
return root; //这是出口2
}
else if(pLeft&&qLeft) //都在左边
{
return lowestCommonAncestor(root->left, p, q); //去找出口,找到直接结束了
}
else //都在右边
{
return lowestCommonAncestor(root->right, p, q);
}
}
};
难点:虽然思路很清晰,但是考验代码和递归的控制能力。(找到该节点就不再递归了,应该立马结束,应该怎么控制?可以有||,可以直接return)
好好理解,希望能举一反三!!!
思路2:
分别找到p,q两个结点的路径,当成链表相交问题。长链表先走差距步,然后再一块走。地址相同结点即为祖先结点。
class Solution {
public:
bool GetPath(TreeNode* root, stack<TreeNode*>& path, TreeNode* x)
{
if(root == nullptr)
{
return false;
}
path.push(root);
if(root == x)
{
return true;
}
if(GetPath(root->left, path, x))
{
return true;
}
if(GetPath(root->right, path, x))
{
return true;
}
path.pop();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> pPath;
stack<TreeNode*> qPath;
GetPath(root, pPath, p);
GetPath(root, 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 qPath.top();
}
};
难点:
- 递归去找结点的路径,这个方法也很巧妙,注意揣摩。