剑指Offer68-Ⅱ—二叉树的最近公共祖先

剑指Offer68-Ⅱ

题意

解题思路

本题与上一题的唯一不同就是上一题是二叉搜索树,本题是二叉树。

若 root 是 p, q 的 最近公共祖先 ,则只可能为以下情况之一:

  1. p 和 q 在 root 的子树中,且分列 root 的 异侧(即分别在左、右子树中);
  2. p = root ,且 q 在 root 的左或右子树中;
  3. 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;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之所向便是光v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值