题意
解题思路
本题与上一题的唯一不同就是上一题是二叉搜索树,本题是二叉树。
若 root 是 p, q 的 最近公共祖先 ,则只可能为以下情况之一:
- p 和 q 在 root 的子树中,且分列 root 的 异侧(即分别在左、右子树中);
- p = root ,且 q 在 root 的左或右子树中;
- q=root ,且 p 在 root 的左或右子树中;
C++实现
class Solution
{
public:
TreeNode* find_p_or_q(TreeNode* root,TreeNode* p,TreeNode* q)
{
if(root==nullptr || root==p || root==q)
{
//如果root为空,说明已经越过叶子节点了,还没找到p、q,就返回空就可以了
//如果root就是p或者q,则已经找到p或者q了,返回之即可
return root;
}
TreeNode* left_res = find_p_or_q(root->left,p,q); //去root的左子树去找p或者q
TreeNode* right_res= find_p_or_q(root->right,p,q); //去root的右子树去找p或者q
if(left_res!=nullptr&&right_res!=nullptr)
{
//如果在root的左右子树中都找到了p、q,说明root就是最近的公共祖先
return root;
}
else if(left_res==nullptr&&right_res==nullptr)
{
//在左右子树中都没有找到p、q,说明以root为根的这颗树中就没有p和q
return nullptr;
}
else if(left_res!=nullptr&&right_res==nullptr)
{
//root的右子树没有p、q,返回左子树的结果即可
return left_res;
}
else
{
return right_res;
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
return find_p_or_q(root,p,q);
}
};
法2:记录节点的父节点
思路
我们可以用哈希表存储所有节点的父节点。
然后我们就可以利用节点的父节点信息从 p 结点开始不断往上跳,并记录已经访问过的节点。
再从 q 节点开始不断往上跳,如果碰到已经访问过的节点,那么这个节点就是我们要找的最近公共祖先。
class Solution
{
public:
unordered_map<int,TreeNode*> father;
unordered_map<int,bool> visited;
//该函数作用是记录以root为根节点的树 的所有节点的 父节点
void dfs(TreeNode* root)
{
if(root->left!=nullptr)
{
father[root->left->val] = root; //记录root的左孩子的父亲是root
// 注意,这里不能改变root,root=root->left,在这里不可以。因为下面还要访问root的右孩子
//如果在这里改变root的指向了,那么就会出错。
dfs(root->left); //继续递归
}
if(root->right!=nullptr)
{
father[root->right->val] = root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
//法2:用哈希表存储父节点
father[root->val] = nullptr;
dfs(root);
//从p开始往上遍历
while(nullptr!=p)
{
visited[p->val] = true;
p = father[p->val];
}
//从q开始往上遍历
while(nullptr!=q)
{
if(visited[q->val]==true)
{
//q这个节点遍历过了,那么就是最近的公共祖先
return q;
}
visited[q->val] = true;
q = father[q->val];
}
return nullptr;
}
};