Leetcode: 236.二叉树的最近公共祖先

Leetcode:236.二叉树的公共祖先

题目描述

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例

示例 1
在这里插入图片描述

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例2

在这里插入图片描述

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3

输入:root = [1,2], p = 1, q = 2
输出:1

思路分析

规则:两个结点中,如果一个是左子树结点,一个是右子树结点,那么它就是公共祖先.
注意:如果两个结点中有一个结点为根结点,那么这个结点也为公共祖先.
解法一:
从根开始递归寻找,如果这两个结点在根的左右子树,就说明对于这棵树来说,这个根就为这两个结点的公共祖先,如果没找到,就说明此时p,q在这棵树根的同一子树中,此时有两种情况:
1:如果p,q结点在左子树,就重新递归到左子树寻找p,q的位置打是否在左子树根的一左一右.

2:如果p,q结点在右子树,就重新递归到右子树寻找p,q的位置是否在右子树根的一左一右.

查找函数:
1: 如果根为空,返回false.
2: 如果找到了,就返回true.
3: 如果没找到,就从root的左子树去找.
4: 左子树没找到就从root的右子树去找.

解法二:
a :我们可以采用类似前序遍历的方式利用栈来保存两个结点的祖先路径.
1: 如果为空树,就返回false.
2: 如果不为空,先入栈保存.
3: 如果找到了,就返回true.
4: 如果没找到,就递归从该树的左子树寻找,左子树没找到,就递归从该树的右子树寻找.
5:如果递归到一棵树的左右子树都为空,并且依旧还没找到,就说明这个结点root绝对不是目标结点的公共路径,所以要将这个结点出栈.并返回false.

b : 当得到两个结点的祖先路径,我们要将保存两个结点中祖先路径较长的栈出栈,直到和另一个栈的路径长度相等.( 出栈的结点一定没有公共祖先).
1:当两个保存祖先路径栈的长度相等,比较栈顶结点,如果相等则说明这个结点就为两个结点的公共祖先.

2:如果不相等则两个栈出栈继续比较栈顶元素,直到两个栈为空.

代码实现

解法一:

class Solution {
public:
    bool find( TreeNode* sub,TreeNode* x )
    {
         if( sub == nullptr )
         {
             return false;
         }
         return ( sub == x ) || find( sub->left,x) || find( sub->right,x);
        
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if( root == nullptr )
        {
            return nullptr;
        }
        if( root == p || root == q )
        {
            return root;
        }
        bool pInLeft,pInRight,qInLeft,qInRight;
        pInLeft = find( root->left,p);
        pInRight = !pInLeft;
        
        qInLeft = find( root->left,q);
        qInRight = !qInLeft;

        if( pInLeft && qInRight || pInRight && qInLeft )
        {
            return root;
        }     
        else if ( pInLeft && qInLeft )
        {
            return  lowestCommonAncestor(root->left,p,q);
        }
        else if ( pInRight && qInRight )
        {
            return  lowestCommonAncestor(root->right,p,q);
        }
        else{
            return nullptr;
        }
    }
};

解法二:

class Solution {
public:
    bool find( TreeNode* root, TreeNode* x, stack<TreeNode*>& path )
    {
        if( root == nullptr )
        {
            return false;
        }
        path.push(root);
        if( root  == x  )
        {
            return true;
        }
        //如果左子树找到了就返回不再遍历
        if( find( root->left,x,path) )
        {
            return true;
        } 
        //如果左子树失败了,就从右子树找.
        else if ( find( root->right,x,path))
        {
            return true;
        }
        else
        {
          //如果都没找
          path.pop();
           return false;
        }
    }
    
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath,qPath;
        find( root,p,pPath );
        find( root,q,qPath );
        
        //转换为链表相交,循环过后让两个祖先栈的路径长度相等.
        while ( pPath.size() != qPath.size() )
        {
            if( pPath.size() > qPath.size() )
            {
                pPath.pop();
            }
            else
            {
                qPath.pop();   
            }
        }
        while( pPath.size() != 0 && qPath.size() != 0  )
        {
             if( pPath.top() == qPath.top() )
             {
                 return pPath.top();
             }
             pPath.pop();
             qPath.pop();
        }
        return nullptr;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

暂停更新

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

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

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

打赏作者

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

抵扣说明:

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

余额充值