树:寻找最近的公共祖先

前引:

树可以分为好几种:普通树,二叉树(二叉链,三叉链),二叉搜索树等等,今天我们,讨论的的问题可就和就几种树有关。

我们先易后难,讨论二叉搜索树! 

且所有的输入满足

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的树中

二叉搜索树篇: 

题解:

由于二叉搜索树是有序的,左子树全部小于根节点,右子树全部大于根节点。

倘若我们要查找p,q节点的公共祖先。其中根节点的值等于p节点的值或者等于q节点的值,根就是我们要查找的公共祖先。

例如:p为5,q为2;当我们查找到2的时候,就可以停止;由于我们从树根节点遍历下来,且p,q节点均存在于树中,我们就可以认为p一定存在于q的子树中。

当根节点的值大于p和q的值时,我们就可以只遍历左子树, 同样,当根节点的值小于p和q的值时,我们就可以只遍历右子树;

倘若一个比根节点的值大,一个比根节点的值要小,则肯定一个在左子树,一个在右子树,当前根节点就是我们要找的最近公共祖先 。

整理思路:

  • 当根节点的值大于p和q的值时,我们就可以只遍历左子树
  • 当根节点的值小于p和q的值时,我们就可以只遍历右子树;
  • 否则,当前根节点就是我们要找的最近公共祖先 。

下面看我们的代码实现:

代码实现:

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==nullptr)
          return nullptr;
          
          if(p->val>root->val&&q->val>root->val)
           return  lowestCommonAncestor(root->right,p,q);
           if(p->val<root->val&&q->val<root->val)
           return lowestCommonAncestor(root->left,p,q);   
        
        return root;
    }

二叉树:三叉链结构:

什么是三叉链结构呢?

就是每个子结点都有一个指针,指向父节点。

所以三叉链结构,解决这个问题十分简单。我们只需要在树中找到对应的节点,在通过指向父节点的指针找到最近公共祖先。其实,这有点在两个链表中寻找公共节点的意思。

 由于题库中没有找到这个题,代码就不实现了。

二叉树,普通树

题解:

二叉树和普通树都没有什么特点,所以我们只需要遍历树中的每个节点,倘若我们要查找p,q节点的公共祖先。其中根节点的值等于p节点的值或者等于q节点的值,根就是我们要查找的公共祖先。倘若一个比根节点的值大,一个比根节点的值要小,则肯定一个在左子树,一个在右子树,当前根节点就是我们要找的最近公共祖先 。

代码实现:

//函数功能:在以root为根节点的树中查找是否存在node节点
bool findnode(TreeNode* root,TreeNode* node)
    {
        if(root==nullptr)
          return false;
        if(root->val==node->val)
           return true;
        
          return findnode(root->left,node)||findnode(root->right,node);
                
    }
//寻找公共祖先
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
{
        //其中根节点的值等于p节点的值或者等于q节点的值,根就是我们要查找的公共祖先
         if(root==p||root==q)
           return root;

           bool pleft=findnode(root->left,p);
           bool pright=findnode(root->right,p);
           bool qleft=findnode(root->left,q);
           bool qright=findnode(root->right,q);

          //一个在左子树,一个在右子树,当前根节点就是我们要找的最近公共祖先 。
           if((pleft&&qright)||(pright&&qleft))
              return root;
         //全在左子树,递归到左子树
           if(pleft&&qleft)
           return  lowestCommonAncestor(root->left,p,q);
        //全在右子树,递归到右子树
          if(pright&&qright)
           return  lowestCommonAncestor(root->right,p,q);

            return nullptr;   
}

到这里,我们本文所讲的寻找公共祖先篇已经结束了。

但是在最后一普通树、二叉树一篇中,我们可以看一下时间复杂度是多少呢? O(N)?其实我们遍历一棵树,时间复杂度也就是O(N);但是我们做了许多重复的工作?

我们再看一下这张图:

比如我们要查找的节点为7 和4;我们首先判断以3为根节点的树,遍历完左子树和右子树,发现全在左子树;在判断以5为根节点的树,遍历完左子树和右子树,发现全在右子树。(其实到这里我们已经发现了,6 2  7  4 我们已经遍历了两遍) 直到我们找到7和4.

由于做了重复的工作   第一个N第二次N/2。。。。。。我们可以认为是N^2了。

那可以改善吗?

我们可以借鉴三叉链方法:空间换时间,用容器将路径保存,再寻找最近的公共节点。

下面给出代码实现:

效率有所提高的代码实现:

 //需要特别注意的一点是需要一个保留路径的栈,还需要一个栈,找到节点后,将信息保存;因为本函数使用
    递归用一个栈无法保存信息。

void findnode(TreeNode* root,TreeNode* node,stack<TreeNode*>& tmp,stack<TreeNode*>& s)
    {
        if(root==nullptr)
          return ;
          else
          {
                tmp.push(root);
                if(root->val==node->val)
                {
                     s=tmp;
                }
                  findnode(root->left,node,tmp,s);
                  findnode(root->right,node,tmp,s);
                 tmp.pop();
          }               
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==p||root==q)
           return root;
           stack<TreeNode*>  tmp;
           stack<TreeNode* > pstack;
           stack<TreeNode*>  qstack;

        findnode(root,p,tmp,pstack);
        findnode(root,q,tmp,qstack);

        if(pstack.size()>qstack.size())
        {
            while(pstack.size()!=qstack.size())
            {
                pstack.pop();
            }
        } 
         if(pstack.size()<qstack.size())
        {
            while(pstack.size()!=qstack.size())
            {
                qstack.pop();
            }
        } 
        while(!pstack.empty()&&!qstack.empty()&&pstack.top()->val!=qstack.top()->val)
        {
            pstack.pop();
            qstack.pop();
        }
        if(pstack.empty()||qstack.empty())
          return nullptr;
        return pstack.top();
    }

本篇博文到这里就结束了,谢谢大家的观看!

你们的 【三连】 是给Qyuan最大的肯定!

↓          ↓           ↓

注:如果本篇博客有任何错误和建议,欢迎伙伴们留言,你快说句话啊!

如果需要练习的小伙伴,可以打开下方链接:

二叉搜索树的最近公共祖先

https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/

二叉树的最近公共祖先

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值