代码随想录算法训练营Day20|最大二叉树、合并二叉树、二叉搜索树中的搜索、验证二叉搜索树

本文介绍了如何通过递归和迭代的方式构建最大二叉树,并探讨了在二叉搜索树中搜索节点和验证其有效性的算法,包括中序遍历的应用和终止条件的设定。

最大二叉树

题目:

最大二叉树定义:

二叉树的根是数组中的最大元素

左子树是通过数组中最大值左边部分构造出的最大二叉树。

右子树是通过数组中最大值右边部分构造出的最大二叉树。

通过给定的数组构建最大二叉树,并且输出这个树的节点。

构造二叉树一般采用的是前序遍历,因为先构造中间节点,然后构造左子树和右子树。

>>确定递归函数的参数和返回值

参数传入的是存放元素的数组,返回该数组构造的二叉树的头节点,返回类型是指向节点的指针。

TreeNode* constructMaximumBinaryTree(vector<int>& nums)

>>确定终止条件

题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。

那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。这表示一个数组大小是1的时候,构造了一个新的节点,并返回。

TreeNode * node = new TreeNode(0);
if(nums.size()== 1){
 node->val = nums[0];
 return node;
}

>>确定单层递归的逻辑

1.找到数组中最大的值和对应的下标,最大的值构造根节点,下标用来下一步分割数组。

2.最大值所在的下标左区间构造左子树,这里要判断maxValueIndex > 0,因为要保证左区间至少有一个数值。

3.最大值所在的下标右区间 构造右子树 ,这里要判断maxValueIndex < (nums.size() - 1),确保右区间至少有一个数值。

class Solution{
public:
    TreeNode* constructMaximumBinaryTree(vector<int>&nums){
      TreeNode* node = new TreeNode(0);
      if(nums.size() == 1){
        node->val = nums[0];
        return node;
      }
      //找到数组中最大的值和对应的下标
      int maxValue = 0;
      int maxValueIndex = 0;
      for(int i = 0;i < nums.size();i++){
        if(nums[i] > maxValue){
          maxValue = nums[i];
          maxValueIndex = i;
        }
      }
      node->val = maxValue;
      //最大值所在的下标左区间 构造左子树
      if(maxValueIndex > 0){
        vector<int>newVec(nums.begin(),nums.begin() + maxValueIndex);
        node->left = constructMaximumBinaryTree(newVec);
      }
      //最大值所在的下标右区间,构造右子树
      if(maxValueIndex < (nums.size()-1)){
        vector<int>newVec(nums.begin() + maxValueIndex + 1,nums.end());
        node->right = constructMaximumBinaryTree(newVec);
      }
      return node;
    }
};

优化:

class Solution{
public:
   TreeNode* traversal(vector<int>&nums,int left,int right){
     if(left >= right)return nullptr;

     int maxValueIndex = left;
     for(int i = left + 1;i < rigth;++i){
       if(nums[i] > nums[maxValueIndex])maxValueIndex = i;
     }

     TreeNode* root = new TreeNode(nums[maxValueIndex]);

     root->right = traversal (nums,maxValueIndex + 1,right);

     return root;
   }
   public:
     TreeNode* constructMaximumBinaryTree(vector<int>&nums){
       return traversal(nums,0,nums.size());
     }
};

加if是为了不让空节点进入递归,这样后面终止条件就不需要加入 遇到空节点,如果不用if,后面终止条件就得加上 遇到空节点。

二叉搜索树中的搜索

题目:给定一个二叉搜索树BST的根节点和一个值。在BST中找到节点值等于给定值的节点。返回该节点作为树的子树。如果不存在,返回,NULL。

二叉搜索树:特点有序

A.若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;

B.若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;

C.它的左右子树也为二叉搜索树

本题就是在二叉搜索树中搜索一个节点,看如何遍历。

递归法

1.确定递归函数的参数和返回值

递归函数的参数传入的就是根节点和要搜索的数值,返回的就是以这个搜索数值所在的节点。

代码:

TreeNode* searchBST(TreeNode* root,int val)

2.确定终止条件

如果root为空,或者找到这个数值了,就返回root节点。

if(root == NULL || root -> val == val)return root;

3.确定单层递归的逻辑

因为二叉搜索树的节点是有序的,所以可以有方向的去搜索。

如果root -> val > val,搜索左子树,如果root -> val < val,就搜索右子树,最后如果都没有搜索到,就返回NULL。

代码如下:

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

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;
    }
    return NULL;
  }
};

二叉搜索树的特性记牢,利用它的特性写遍历会很简单。

验证二叉搜索树

题目:给定一个二叉树,判断其是否是一个有效的二叉搜索树。

待判定的特征:

1.节点的左子树只包含小于当前节点的数。

2.节点的右子树只包含大于当前节点的数。

3.所有左子树和右子树自身必须也是二叉搜索树。

思路:

要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。

有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。

递归法

可以递归中序遍历将二叉搜索树变成一个数组,代码:

vector<int>vec;
void traversal(TreeNode* root){
  if(root == NULL)return;
  traversal(root->left);
  vec.push_back(root->val);//将二叉搜素树转换为有序数组
  traversal(root->right);
}

比较一下,看这个数组是否有序,注意:二叉搜索树中不能有重复元素。

traversal(root);
for(int i = 1;i < vec.size();i++){
  //注意要小于等于,搜索树里不能有相同的元素
if(vec[i] <= vec[i - 1])return false;
}
return true;

完整代码:

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:
     bool isValidBST(TreeNode* root){
       vec.clear();
       traversal(root);
       for(int i = 1;i < vec.size();i++){
         if(vec[i] <= vec[i - 1])return false;
       }
       return true;
     }
}

上面转化成数组,然后判断是否有序,也可以不转换,直接在遍历中判断是否有序

注意点:是左子树“所有”节点小于中间节点,不是一个,右子树同理

样例中最小节点  可能是int的最小值,如果这样使用最小的int来比较也是不行的。

可以初始化元素为long long的最小值。

如果阳历中根节点的val可能是long long 的最小值呢?

递归三部曲:

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

要定义一个long long 的全局变量,用来比较遍历的节点是否有序,因为后台测试数据中有int最小值,所以定义为long long的类型,初始化为long long 最小值。

注意递归函数要有bool类型的返回值,只有寻找一条边或一个节点的时候,递归函数会有bool类型的返回值,

本题中,我们寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整棵树,而如果找到了不符合的节点,就立刻返回

long long maxVal = LONG_MIN;
bool isValidBST(TreeNode* root)

确定终止条件

二叉搜索树可以为空

if(root == NULL)return true;

确定单层递归的逻辑

中序遍历,一直更新maxVal,一旦发现maxVal >= root->val,就返回false,注意元素相同时候也要返回false.

bool left = isValidBST(root->left);
//中序遍历,验证遍历的元素是不是从小到大
if(maxVal < root->val)maxVal = root->val;
else return false;

bool right = isValidBST(root->right);
return left && right;

整体代码如下:

class Solution{
public:
  long long maxVal = LONG_MIN;
  bool isValidBST(TreeNode* root){
    if(root == NULL)return true;

    bool left = isValid(root->left);

    if(maxVal < root->val)maxVal = root->val;
    else return false;
    bool right = isValidBST(root->right);


    return left && right; 
  }
};

上面 maxVal用的是long long最小值,原因是后台数据有int最小值测试用例。

class Solution{
public:
   TreeNode* pre =NULL;//用来记录前一个节点
   bool isValidBST(TreeNode* root){
     if(root == NULL)return true;
     bool left = isValidBST(root->left);

     if(pre != NULL && pre->val >= root->val)retrun false;
     pre = root;//记录前一个节点

     bool right = isValidBST(root->right);
     return left && right;
   }
};

迭代法

迭代法模拟二叉树中序遍历

迭代法中序遍历稍加改动就可以:

class Solution{
public:
  bool isValidBST(TreeNode* root){
    stack<TreeNode*>st;
    TreeNode* cur = root;
    TreeNode* pre = NULL;//记录前一个节点
    while(cur != NULL||st.empty()){
      if(cur != NULL){
        st.push(cur);
        sur = cur->left;
      }else{
        cur = st.top();
        st.pop();
        if(pre != NULL && cur->val <= pre->val)
        return false;
        pre = cur;//保存前一个访问的节点

        cur = cur->right;
      }
    }
    return true;
  }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值