【精选高频面试题】:一篇搞懂---求二叉树属性以及公共祖先问题

求二叉树的属性

Leetcode.700 二叉搜索树的搜索

700. 二叉搜索树中的搜索 - 力扣(LeetCode) (leetcode-cn.com)

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

例如,

给定二叉搜索树:

    4
   / \
  2   7
 / \
1   3

和值: 2
你应该返回如下子树:

  2     
 / \   
1   3

在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。

思路:首先我们先了解一下二叉搜索树的性质:根节点左边的节点值始终小于根节点的值,根节点右边的节点的值始终大于根节点,所以我们可以根据这个性质来进行搜索。当搜索值大于节点值,那么就直接搜索节点值的右子树,反之搜索左子树。

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
         if(root==NULL||root->val==val)return root;
         if(root->val>val) return searchBST(root->left,val);
         if(root->val<val)return searchBST(root->right,val);
        return NULL;
    }
};
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
       while(root!=NULL){
           if(root->val>val)root=root->left;
           else if(root->val<val)root=root->right;
           else  return root;
       }
        
    }
};

Leetcode.98.判断二叉搜索树

98. 验证二叉搜索树 - 力扣(LeetCode) (leetcode-cn.com)

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:root = [2,1,3]
输出:true
示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

**思路:**如果判断一个树是否是平衡二叉树?当一个节点的左子树的所有节点值要节点值,右子树的节点值要大于节点值。所以我们要比较左中右的节点值,所以我们要采用中序遍历。既然要比较,所以我们要初始化最小值用来更新最小值。【注意,在测试用例中,传入树的最小值为INT_MIN,所以我们要采用longlong min,但是 为了预防下一次测试用例采用longlong min ,我们就可以设置一个pre节点值为NULL,之后直接更新这个节点值】

//递归法
class Solution {
public:
    TreeNode* pre=NULL;
    bool isValidBST(TreeNode* root) {
       if(root==NULL)return true;
       bool left=isValidBST(root->left);
        
        if(pre!=NULL&& root->val<=pre->val){
            return false;
        }
        else{
            pre=root;
        }
        bool right=isValidBST(root->right);
        
        return left&&right;
    }
};
//中序遍历迭代法
class Solution {
public:
    bool isValidBST(TreeNode* root) {
       stack<TreeNode*>st;
        TreeNode* cur=root;
        TreeNdoe* pre=NULL;
        while(cur!=NULL||!st.empty()){
            if(cur!=NULL){
                st.push(cur);
                cur->left;
            }
            else:{
                cur=st.top();
                st.pop();
                if(pre!=NULL&&cur->val<=pre->val){
                    return false;
                }
                else{
                    pre=cur;
                }
                cur=cur->right;
            }
        }
        return true;
    }
};

Leetcode.530.二叉搜索树的最小绝对差

530. 二叉搜索树的最小绝对差 - 力扣(LeetCode) (leetcode-cn.com)

给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。

示例:

输入:

1

3
/
2

输出:
1

解释:
最小绝对差为 1,其中 2 和 1 的差的绝对值为 1(或者 2 和 3)。

**思路:**当我们看到二叉搜索树的时候我们就要想到其性质:按中遍历输出是有序的,所以呢,方法一:我们就把这个搜索二叉树转换为有序数组,然后一一进行比较。

class Solution {
private:
    vector<int>vec;
    void trval(TreeNode*root){
        if(root==NULL)return ;
        if(root->left) trval(root->left);
        vec.push_back(root->val);
        if(root->right)trval(root->right);
    }
public:
    int getMinimumDifference(TreeNode* root) {
         int result=INT_MAX;
         trval(root);
         for(int i=1;i<vec.size();i++){
             result=min(result,vec[i]-vec[i-1]);
         }
         return result;

    }
};

方法二:我们可以在遍历中直接比较,但是这个方法需要我们设置前一节点的指针。这个方法与98判断二叉树是否为平衡二叉树方法差不多,只是在处理中间节点的时候的操作逻辑不一样罢了

//递归
class Solution {
public:
    int res=INT_MAX;
    TreeNode*pre;
    void trval(TreeNode* root){
        if(root==nullptr)return ;
        trval(root->left);
        if(pre!=NULL){
            res=min(res,root->val-pre->val);
        }
        pre=root;
        trval(root->right);
    }
    int getMinimumDifference(TreeNode* root) {
        trval(root);
        return res;
    }
};
//迭代
class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
       stack<TreeNode*>st;
       TreeNode* cur=root;
       TreeNode* pre=NULL;
       int res=INT_MAX;
      while(cur!=NULL||!st.empty()){
           if(cur!=NULL){
               st.push(cur);
               cur=cur->left;
           }
           else{
               cur=st.top();
               st.pop();
               if(pre!=NULL){
                   res=min(res,cur->val-pre->val);
               }
               pre=cur;
               cur=cur->right;
           }
       }
       return res;
    }
};

Leetcode.501.二叉搜索树中的众数

501. 二叉搜索树中的众数 - 力扣(LeetCode) (leetcode-cn.com)

给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

假定 BST 有如下定义:

结点左子树中所含结点的值小于等于当前结点的值
结点右子树中所含结点的值大于等于当前结点的值
左子树和右子树都是二叉搜索树
例如:
给定 BST [1,null,2,2],

1

2
/
2
返回[2].

提示:如果众数超过1个,不需考虑输出顺序

进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)

**思路:**这道题有两种解法,第一种就是针对普通的二叉树,我们先遍历二叉树,将其结果使用哈希表存放,再将哈希表转化为vector,之后再对vector进行排序,再遍历vector获取最大值(众数)。第二种解法,是利用搜索二叉树的性质,中序遍历是有序的,将搜索二叉树按中序遍历,将相邻元素进行比较,记录其出现频率。

解法一:

class Solution {
public:
    void trval(TreeNode*root,unordered_map<int,int>&map){
        if(root==NULL)return ;
        trval(root->left);
        map[root->val]++;
        trval(root->right);
        return;
        
    }
    bool static com(const pair<int,int>&a,const pair<int,int>&b){
        return a.second>b.second;
    }
    vector<int> findMode(TreeNode* root) {
        vector<int>result;
        if(root==NULL)return result;
        unordered_map<int,int>map;
        trval(root,map);
        vector<pair<int,int>>vec(map.begin(),map.end());
        sort(vec.begin(),vec.end(),com);
        result.push_back(vec[0].first);
        for(int i=1,i<vec.size();i++){
            if(vec[i].second==vec.[0].second){
                result.push_back(vec[i].first);
            }
            else break;
        }
        return result;
    }
};
class Solution {
private:
    int count;
    int maxvalue;
    TreeNode*pre;
    vector<int>result;
    void trval(TreeNode* cur){
        if(cur==NULL)return;
        trval(cur->left);//左
        if(pre==NULL){
            count=1;
        }
        else if(cur->val==pre->val){
            count++;
        }
        else{
            count=1;
        }
      
        if(maxvalue==count){
            result.push_back(cur->val);
        }
        if(count>maxvalue){
            maxvalue=count;
            result.clear();
            result.push_back(cur->val);
        }
        pre=cur;//中
        trval(cur->right);//右
        
        
    }
    
public:  vector<int> findMode(TreeNode* root) {
         count=0;
         maxvalue=0;
         pre=NULL;
         if(root==NULL)return result;
         trval(root);
         return result;
    }
};

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

236. 二叉树的最近公共祖先 - 力扣(LeetCode) (leetcode-cn.com)

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

百度百科中最近公共祖先的定义为:“对于有根树 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的最近公共祖先,当这个节点的左子树是q,右子树是p,或者左子树为p,右子树为q的时候,则判断成功。

第二,我们采用后续遍历(回溯的过程)来判断节点的左右节点是否含有p,q,最终的结果传至根节点

递归3要素:

第一我们要确认函数的返回类型与参数,按道理是如果找到最小公共祖先的话就直接返回,但是我们还需要递归函数的返回值做逻辑处理,所以不能返回bool ,还是要返回节点值

第二终止条件:当传入节点的值等于p,或,q,或者空的时候return这个节点

第三:单层遍历逻辑:当左右节点都返回值的时候,说明找到了p,q,就返回这个节点值,当节点的左右值,一个为空,一个不为空的时候,就返回不为空的值。当节点的左右值都为空的时候就返回空

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==p||root==q||root==NULL)return root;

        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);

        if(left!=NULL&&right!=NULL)return root;
        if(left==NULL&&right!=NULL)return right;
        else if(left!=NULL&&right==NULL)return left;
        else{
            return NULL;
        }

    }
};

Leetcode.235.搜索二叉树的最小公共祖先

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

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

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

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

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

示例 1:

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

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

说明:

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

思路:按照普通二叉树的做法,我们需要后续遍历整棵树。但是对于搜索二叉树,我们只需要从上往下的单边遍历就可以。因为搜索二叉树的性质,我们就可以确定遍历方向,二对于普通的二叉树,我们无法确定遍历方向。所以再对于这两种不同的树而言,单层遍历逻辑又不一样。具体看代码

class Solution {
public:
    //返回类型:由于我们最后要返回祖先,所以类型为节点类型
     TreeNode* trval((TreeNode* cur, TreeNode* p, TreeNode* q){
         //如果没找到就返回空
         if(cur==NULL)return root;
         
         //根据搜索树的有序我们可以确定祖先的位置
         if(cur->val>q->val&&cur->val>p->val){
             TreeNode* left=trval(cur->left,p,q);
             if(left!=NULL){
                 return left;//只要找到就返回,与普通的二叉树不一样
             }
         }
         if(cur->val<p->val&&cur->val<q->val){
             TreeNode* right=trval(cur->right,p,q);
             if(right!=NULL){
                 return right
             }
         }
         //如果cur的值位于p与q之间,说明就找到了祖先[一句搜索二叉树的性质]
         return cur;
     }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return trval(root,p,q);
    }
};

二叉树属性-系列一:https://blog.csdn.net/weixin_51325294/article/details/120268487

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值