代码随想录算法训练营Day21|二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先

二叉搜索树的最小绝对差

思路1:将二叉搜索树转化成一个有序数组,在有序数组上求两个数的最小值差。

只需要遍历有序数组,统计出最小值差

class Solution{
private:
  vector<int>vec;
  void traversal(TreeNode* root){
    if(root == NULL)return;
    traversal(root->left);
    vec.push_back(root->val);//将二叉搜索树转化成有序数组
    traversal(root->right);
  }
  public:
    int getMinimumDifference(TreeNode* root){
        vec.clear();
        traversal(root);
        if(vec.size()<2)return 0;
        int result = INT_MAX;
        for(int i = 1; i < vec.size();i++){
            result = min(result,vec[i] - vec[i - 1]);
        }
        return result;
    }
};

思路2:一遍用中序遍历,一边计算差值,需要用一个pre节点记录cur节点的前一个节点。

class Solution{
private:
  int result = INT_MAX;
  TreeNode* pre = NULL;
  void traversal(TreeNode* cur){
    if(cur == NULL)return;
    traversal(cur->left);
    if(pre != NULL){
        result = min(result,cur->val - pre->val);
    }
    pre = cur;//记录前一个
    traversal(cur->right);//右
  }
  public:
     int getMinimumDifference(TreeNode* root){
        traversal(root);
        return result;
     }
};

中序遍历的一种迭代法

class Solution{
public:
   int getMinimumDifference(TreeNode* root){
    stack<TreeNode*>st;
    TreeNode* cur = root;
    TreeNode* pre = NULL;
    int result = 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){
                result = min(result,cur->val - pre->val);
            }
            pre = cur;
            cur = cur->right;
        }
    }
    return result;
   }
};

二叉搜索树的特点“有序”!!!递归遍历中前后两个指针的记录!!!

二叉搜索树中的众数

1.不是二叉搜索树的话,把树遍历,用map统计频率,将频率进行排序,最后取前面高频的元素的集合。

>>遍历方法任选

>>对map里频率进行排序

>>取前面高频的元素

class Solution{
preivate:

void searchBST(TreeNode* cur,unordered_map<int,int>&map){//前序遍历
if(cur == NULL)return;
map[cur->val]++;//统计元素频率
search(cur->left,map);
search(cur->right,map);
return ;
}
bool static cmp(const pair<int,int>& a,const pair<int,int>& b){
    return a.second > b.second;
}
public:
   vector<int>findMode(TreeNode* root){
      unordered_map<int,int>map;
      vector<int>result;
      if(root == NULL)return result;
      searchBST(root,map);
      vector<pair<int,int>>vec(map.begin(),map.end());
      sort(vec.begin(),vec.end(),cmp);
      result.push_back(vec[0].first);
      for(int i = 1;i < vec.size(); i++){
        //取最高的放到result数组中
        if(vec[i].second == vec[0].second)result.push_back(vec[i].first);
        else break;
      }
      return result;
   }
}

2.是二叉搜索树,中序遍历是有序的

遍历过程中,相邻元素进行比较,把出现频率高的元素输出

可以使用前后双指针pre和cur对树进行操作。

而且初始化pre = NULL从第一个元素开始比较

maxCount和上一个最大频率相等,result.push_back放进结果集,如果大于上一个,result.clear清空结果集,前面的都清除,把这个再push_back(),

class Solution{
private:
   int maxCount = 0;
   TreeNode* pre = NULL;
   vector<int>result;
   void searchBST(TreeNode* cur){
    if(cur == NULL)return;

    searchBST(cur->left);

    if(pre == NULL){
        count = 1;
    }else if(pre->val == cur->val){
        count++;
    }else{
        count = 1;
    }
    pre = cur;

    if(count == maxCount){
        result.push_back(cur->val);
    }

    if(count > maxCount){
        maxCount = count;
        result.clear();//清空result
        result.push_back(cur->val);
    }

    searchBST(cur->right);
    return;
   }

   public:
      vector<int>findMode(TreeNode* root){
        count = 0;
        maxCount = 0;
        pre = NULL;
        result.clear();

        searchBST(root);
        return result;
      }
};

迭代法,把中序转成迭代,中间节点的处理逻辑完全一样。

class Solution{
public:
   vector<int>findMode(TreeNode* root){
       stack<TreeNode*>st;
       TreeNode* cur = root;
       TreeNode* pre = NULL;
       int maxCount = 0;//最大频率
       int count = 0;//统计频率
       vector<int>result;
       while(cur != NULL || !st.empty()){
          if(cur != NULL){  //指针来访问节点,访问到最底层
             st.push(cur);//将访问的节点放进栈
             cur = cur->left;
          }else{
            cur = st.top();
            st.pop();              //中
            if(pre == NULL){//第一个节点
                count = 1;
            }else if(pre->val == cur->val){//与前一个节点数值相同
                count++;
            }else{//与前一个节点不同
                count = 1;
            }
            if(count == maxCount){//如果和最大值相同,放进result
                result.push_back(cur->val);
            }

            if(count > maxCount){//如果计数大于最大值频率
                maxCount = count;//更新最大频率
                result.clear();//清空result,让之前result里的元素失效
                result.push_back(cur->val);
            }
            pre = cur;
            cur = cur->right;//右
          }

       }
       return result;
   }
};

二叉树的最近公共祖先

思路:一种情况是,该节点的左子树出现节点p,右子树出现节点q。另一种,节点p即为节点p和节点q的最近公共祖先。

二叉树节点数值是不重复的,所以p和q一定存在。节点本身p(q),它拥有一个子孙节点q(p).

第一种情况实现,顺带实现了情况二。

递归三部曲:

>>确定递归函数返回值及参数。

需要递归函数返回值,来告诉我们是否找到节点q或者p,那么返回值为bool类型就可以了。

但我们还要返回最近公共节点,可以利用上题目中返回值是TreeNode* ,那么如果遇到p或者q,就把q或者p返回,返回值不为空,就说明找到了q或者p.

TreeNode* lowestCommonAncestor(TreeNode* root,TreeNode* p,TreeNode* q)

>>确定终止条件

遇到空的话,因为树是空的了,所以返回空。遇到q,p,将其返回。这个返回值,后面在中节点的处理过程中会用到。

if(root == q || root == p || root == NULL)return root;

>>确定单层递归

值得注意的是,本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题我们依然要遍历树的所有节点。

在递归函数有返回值的情况下,如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left,right接住返回值,这个left,right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也就是回溯)。

搜索一条边:

if(递归函数(root->left))return ;
if(递归函数(root->right)) return ;

搜索整个树的写法:

left = 递归函数(root->left);//左
right = 递归函数(root->right);//右
left与right的逻辑处理;//中

用left和right接住左子树和右子树的返回值,代码如下:

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

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

如果left和right都不为空,说明此时root就是最近公共节点

如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之亦然。

if(left == NULL && right != NULL)return right;

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

else {//left == NULL && right == NULL

return NULL;

}

class Solution{
public:
   TreeNode* lowestCommonAncestor(TreeNode* root,TreeNode* p,TreeNode* q){
    if(root == q || root == p || 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{//left == NULL && right == NULL
        return NULL;
    }
   }
};

精简后:

class Solution{
public:
  TreeNode* lowestCommonAncestor(TreeNode* root,TreeNode* p,TreeNode* q){
    if(root == q || root == p || 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)return right;
    return left;
  }
};

求最小公共祖先,需要从底层往上遍历,那么二叉树,只能通过后序遍历,实现从底向上的遍历方式。

在回溯过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值,也就是代码中国的left和right做逻辑判断。

要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。

可以说这里每一步,都是有难度的,都需要对二叉树,递归和回溯有一定的理解。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值