剑指Offer68-II.二叉树的最近公共祖先

  • 题目:剑指Offer68-II.二叉树的最近公共祖先
    给定一棵二叉树和两个目标节点p,q,找到p,q的最近公共祖先;
    剑指Offer68-Ⅰ.二叉搜索树的最近公共祖先相比,无法使用有序这一点,因此只能爆搜,找到p,q,再找到最近公共祖先,再一层一层传回根;

  • 思路:
    1.dfs:时间O(n):遍历所有节点,唯一不需要搜的节点是p,q的子孙,因为我们目标是找p,q的祖先,至于p,q的子孙长什么样无所谓,所以找到p或q就直接往上层回退了,无需递归左右孩子,空间O(n):最坏情况是退化为单链表时,递归深度为n
    ①总的来说,要首先找到p,q,才能往回找祖先,因此一定是后序遍历;
    ②递归函数的返回值:需要用bool表示是否找到了最近公共祖先,只有p,q分别来自左右子树才能记录res,因此这种写法无法包括p本身是q的祖先或q是p的祖先的情况;如果非要这么写,由于这种情况下只会遇到p或q中的一个,只能再设置一个全局变量,用于标志这种情况

//不能覆盖p,q其中一个是另一个的祖先的情况
class Solution {
public:
    TreeNode* res;
    bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root) return false;
        if (root == p || root == q) return true;//如果非要这么写

        bool l = dfs(root->left, p, q);
        bool r = dfs(root->right, p, q);
        if (l && r) {//找到了最近公共祖先
            res = root;//记录下来
            return true;
        }
        else if (l || r) return true;//其中一棵子树找到了p或q,往上层传递
        else return false;//两棵废物子树,都找不到p,q
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        dfs(root, p, q);

        return res;
    }
};

//强行这么写,太麻烦了
class Solution {
public:
    TreeNode* res, *res2;//true对应res,false对应res2
    bool flag;//true表示p,q分别来自左右子树,false表示p,q其中一个是另一个的祖先这种情况
    bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root) return false;
        if (root == p || root == q) {
            res2 = root;
            return true;
        }

        bool l = dfs(root->left, p, q);
        bool r = dfs(root->right, p, q);
        if (l && r) {//找到了最近公共祖先
            flag = true;
            res = root;//记录下来
            return true;
        }
        else if (l || r) return true;//其中一棵子树找到了p或q,往上层传递
        else return false;//两棵废物子树,都找不到p,q
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        flag = false;
        dfs(root, p, q);
        if (flag == true) return res; 
        else return res2;
        
    }
};

因此,尝试返回值用TreeNode*,返回空说明当前子树为空,或者当前子树找不到目标p或q;返回非空说明当前子树找到了目标p或q:包括本身root是p或q,以及左右子树找到了p或q;
③在回溯的过程中,必然要遍历整颗二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要利用递归函数的返回值作逻辑处理:

//遍历整棵树
auto l = dfs(root->left);
auto r = dfs(root->right);
使用l和r作逻辑处理,

//遍历某条边
if (dfs(root->left)) return ...
if (dfs(root->right)) return ...

④当l,r均为非空(下图节点7),很好理解,说明左右子树各找到一个目标;若其中一个为空时(节点6,10,8),应返回另一个,是因为要把p,q逐层传上去(节点6),或者找到最近公共祖先后,把该祖先逐层传上去(节点10,8);
在这里插入图片描述

class Solution {
public:
    //返回nullptr表示:当前节点为空,或当前子树找不到p或q这两个目标节点
    //返回非空,说明当前节点就是目标节点,或当前子树找到了目标节点,或目标节点的祖先(甚至祖先的祖先。。一直传回根节点)
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root || root == p || root == q) return root;//本身root为空 或 root自己就是目标节点
        
        //否则就要尝试去自己的左右子树中找p,q
        auto l = lowestCommonAncestor(root->left, p, q);
        auto r = lowestCommonAncestor(root->right, p, q);
        if (l == nullptr && r == nullptr) return nullptr;//说明左右子树都是废物
        else if (l == nullptr) return r;//左子树找到了p或q:此时的r可能直接是p,q,或pq的祖先,或祖先的祖先。。。一直往上传
        else if (r == nullptr) return l;//右子树找到了p或q,同上
        else return root;//左右子树都找到了(各找到一个p,q),说明当前节点就是最近公共祖先,随后在往根回退的途中,肯定只能进入两个else if分支,因为已经找到最近公共祖先了,其它子树必定return nullptr,就这样一路返回根节点
       
    }
};
  • 总结:
    细节很多,需要再刷
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值