二叉数题型2

目录

二叉搜索树的众数

二叉树的最近公共祖先

修剪二叉树


二叉搜索树的众数

问题描述:

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。如果树中有不止一个众数,可以按 任意顺序 返回。

 思路1:运用STL中的容器map,vector来辅助解决。通过遍历二叉树,将其放入map中,map可以统计每个key对应的value值。但map是对key排序的,所以再将每个键值对放入vector中,让vector对value值进行排序。

对应代码:

class Solution {
public:
    map<int,int> m_;
    void _preoder(TreeNode* root)
    {
        if(root==nullptr)
          return;
        if(root!=nullptr)
          m_[root->val]++;
        _preoder(root->left);
        _preoder(root->right);
    }
    bool static cmp(const pair<int,int>&a,const pair<int,int>& b)
    {
        return a.second>b.second;//按从大到小排序
    }
    vector<int> findMode(TreeNode* root) {
    
      _preoder(root);
      vector<pair<int,int>> v_(m_.begin(),m_.end());
      sort(v_.begin(),v_.end(),cmp);
      vector<int> result;
      result.push_back(v_[0].first);     //其中一个众数
      for(int i=1;i<v_.size();i++)
      {
          if(v_[i].second==v_[i-1].second)// 可能有多个众数
           result.push_back(v_[i].first);
          else
           break;
      }
      return result; 
    }
};

思路2因为是二叉搜索数,所以按中序遍历的顺序去遍历时,是按从小到大的顺序访问的,可以去比较相近的两个元素是否相等,统计最大的次数。

中序遍历的框架与上文介绍的是一样的:

TreeNode* pre=nullptr;
void _findMode(TreeNode* cur)
{
    if(cur==nullptr)
      return;
    _findMode(cur->left);
   if(pre==nullptr)
    cur=pre;
   else
  //处理节点
     _findMode(cur->right);
}

用两个变量分别记录当前遍历到的元素个数,和当前最多的元素的个数。将出现最多的元素放入vector中。例如:

class Solution {
public:
 TreeNode* pre=nullptr;  
 vector<int> result;
 int count=0;
 int maxcount=0;
void _findMode(TreeNode* cur)
{
    if(cur==nullptr)
      return;
    _findMode(cur->left);
    if(pre==nullptr)         //若pre为空,则cur刚找到最小的元素,此时count置位1
      count=1;
    else if(pre->val==cur->val)  //前后两元素相同,则count++
      count++;
    else                      //重新计数
     count=1;
    pre=cur;
    if(count==maxcount)      //说明出现了多个相同次数的元素
     result.push_back(cur->val);
    if(count>maxcount)       //后面出现的元素次数更大,把之前加入的元素清空,重新加入
    {
        result.clear();
        result.push_back(cur->val);
        maxcount=count;
    }

     _findMode(cur->right);
}
    vector<int> findMode(TreeNode* root) {
    
    TreeNode* cur=root;
    _findMode(cur);
    return result;
    }
};

这种方法只需要遍历一次二叉树即可。

二叉树的最近公共祖先

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

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

思路1:分别寻找p,q是在当前的节点的左边还是右边。因为题目给定的p,q两个节点必定存在公共祖先。情况1:当前节点就是p或者q,那么当前节点就是祖先。情况 2:p,q分别在当前节点的两边,那么当前节点就是祖先。情况3:p,q节点都在当前节点的左边或者右边,那么当前节点往左或者右边遍历,重复上面的的操作。

class Solution {
public:
  bool _istree(TreeNode*root,TreeNode*node)  //确定p,q节点是否在当前节点下
  {
      if(root==nullptr)
        return false;
     if(node==root)
       return true;
    return _istree(root->left,node)||_istree(root->right,node);
      
  }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p==root||q==root)
          return root;
        bool pinleft=_istree(root->left,p);
        bool pinright=!pinleft;
        bool qinleft=_istree(root->left,q);
        bool 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;
    }
};

上面的思路很好理解,但是若p,q节点都是叶子节点,都是兄弟节点。那么上面的代码时间复杂度就会很高。 (lowestCommonAncestor)每往后走一步,(_istree)都要遍历到最后才能找到,(lowestCommonAncestor)也要走到最后,就是p,q的父亲节点才能结束。

思路2:若是可以通过自底向上去查找就更容易了,可以模拟二叉树后序遍历的过程。把找到的结果返回给上一层。

例如:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {

        if(root==nullptr||root==p||root==q)//终止条件
         return root;
        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);

        if(left!=nullptr&&right!=nullptr)   //找到了结果,返回
         return root;
        else if(left==nullptr&&right==nullptr)
         return nullptr;
        else if(left!=nullptr)
         return left;
        else
         return right;
        
    }
};

思路3:从头节点开始找到每个节点的路径,然后再去找到路径上的共同节点。

class Solution {
public:
     bool findpath(TreeNode* root,TreeNode* x,stack<TreeNode*>& Path)
     {   
         if(root==NULL)
          return false;
         Path.push(root);
         if(root==x)
           return true;
         if(findpath(root->left,x,Path))
            return true;
         if(findpath(root->right,x,Path))
            return true;

        Path.pop();
        return false;
       
     }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        
        stack<TreeNode*>ppath,qpath;
        findpath(root,p,ppath);
        findpath(root,q,qpath);
       while(ppath.size()>qpath.size())
       {
           ppath.pop();
       }
       while(ppath.size()<qpath.size())
       {
           qpath.pop();
       }
        while(ppath.top()!=qpath.top())
        {
            ppath.pop();
            qpath.pop();
        }
        return ppath.top();
    }
};

修剪二叉树

题目描述:给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 

思路:

 

 可能很容易写出这样的代码:直接判断当前节点是否满足条件,如不满足,这直接将左孩子或右孩子返回。

例如:

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        
    if(root==nullptr)
     return nullptr;
    if(root->val<low)
      return right;
    if(root->val>high)
      return left;

     root->left=trimBST(root->left,low,high); //条件满足,则继续递归当前节点的左右孩子,对其修剪
     root->right=trimBST(root->right,low,high);
     return root;                             //返回当前节点给上一层
 
    }
};

这样的话并没有把节点值3给删除点,正确的做法是,例如当前节点的值已经小于low,那么它的右孩子也可能小于low或者它的右子树中有节点小于low.它的左子树肯定不满足条件了。后面要对它的右子树进行修建,接受它的右子树的返回值,再将结果返回给上一层。

代码:

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        
    if(root==nullptr)
     return nullptr;
    if(root->val<low)
    {
        TreeNode* right=trimBST(root->right,low,high);  //对右子树进行修剪
        return right;                  //当前节点已不满足条件,接收右子树返回值,再返回
    } 
    if(root->val>high)
    {
        TreeNode* left=trimBST(root->left,low,high);   //对左子树修剪
        return left;                   //当前节点已不满足条件,接收左子树返回值,再返回
    }

    root->left=trimBST(root->left,low,high);//条件满足,则继续递归当前节点的左右孩子,对其修剪
    root->right=trimBST(root->right,low,high);
    return root;                       //返回当前节点给上一层

    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值