二叉树最近公共祖先

最近公共祖先

力扣235/LCR 193. 二叉搜索树的最近公共祖先

力扣236/LCR 194. 二叉树的最近公共祖先

核心点在于分清楚可能存在的三种情况:

  • p,q位于最近祖先的左右子树
  • p,q位于最近祖先的左子树
  • p,q位于最近祖先的右子树

在这里插入图片描述

1 二叉搜索树

对于二叉搜索树,我们可以根据其特性以及p,q的值关系来判断属于以上哪种情况。如:

  • p,q都小于root,则满足情况2
  • p,q都大于root,则满足情况3
  • 其他情况,则p,q位于root的两侧,说明root位于“分岔路口”,满足情况1

若保证p小于q(或p大于q),可以简化判断逻辑。即root小于较小值则搜寻右子树,root大于较大值则搜寻左子树。

思路:首先使得p小于q。然后根据值的大小关系深入迭代左右子树,直到“分岔”,此时root就是最近公共祖先。

时间复杂度为O(N),N为二叉树节点数,最坏情况下,需要遍历所有节点,如链树。

代码如下,其中返回值代表的是最近公共祖先:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p->val > q->val)
            swap(p, q);
        while(root) {
            if(q->val < root->val) // 较大值小于root,说明位于左子树
                root = root->left;
            else if(p->val > root->val) // 较小值大于root,说明位于右子树
                root = root->right;
            else break;    
        }
        return root;
    }
};

递归写法:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p->val > q->val)
            swap(p, q);
        return dfs(root, p, q);
    }

    TreeNode* dfs(TreeNode* &root, TreeNode* &p, TreeNode* &q) {
        if(!root) 
            return NULL;

        if(q->val < root->val)
            return dfs(root->left, p, q);
        else if(p->val > root->val)
            return dfs(root->right, p, q);
        return root;
    }
};

2 二叉树

对于二叉树,没有二叉搜索树的性质可以使用,也就没法选择一个子树迭代,需要分头行动(递归左右子树)。同时,由于递归是顺序进行的,所以一旦遇到p或者q,则他自己就是离自己最近的祖先,就不用继续递归其后面的节点了。

在这里插入图片描述

思路:

  • root等于pq,则直接返回该root,代表已判断存在,不需要再继续找其左右子树了。(判断存不存在就行了,判断某root是不是最近祖先的核心还是根据最上面的三点)
  • 否则继续判断某root判断其左右子树是否存在p,q,得到结果left,right
  • 通过left,right的关系来判断某root是不是最近公共祖先
    • left不存在,right不存在,返回NULL(叶子结点)。
    • left存在,righ存在,说明某root就是最近公共祖先。如当3root时,p,q分别为5,0的情况。
    • left存在,right不存在,说明p,q都在某root的左子树(右边找完了但没有,左边虽然没找完【因为找到一个就直接返回了】,但是另一个肯定在左边,同理下一条),所以left就是最近公共祖先。如当3rootp,q分别为5,2的情况。
    • right存在,left不存在,说明p,q都在某root的右子树,所以right就是最近公共祖先。如当5rootp,q分别为2,4的情况

上述判断left和right的逻辑略微复杂,逻辑简化的时候也需要注意下小细节,见代码。

时间复杂度为O(N),N为二叉树节点数,最坏情况下,需要遍历所有节点,如p,q分别为4,8或链树。

代码如下:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return dfs(root, p, q);
    }

    TreeNode* dfs(TreeNode* &root, TreeNode* &p, TreeNode* &q) {
        if(!root || root == p || root == q)
            return root;
        TreeNode* left = dfs(root->left, p, q);
        TreeNode* right = dfs(root->right, p, q);

        if(!left && !right) // 都不存在
            return NULL;
        if(!left)		    // left不存在,说明right存在,直接返回right
            				// 注意不能 if(left) return left,因为若right也存在,则应该返回root。第一条if只能保证后续left和right不同时为空
            return right;
        if(!right)          // right不存在,说明left存在,直接返回left
            return left;
        return root;        // (left && right) 的情况,root为最近公共祖先
    }
};
  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值