Binary search tree sequence problem (catch all at once)

content

1. Verify the preorder sequence of the binary search tree

2. Preorder traversal to restore binary search tree

3. Post-order traversal of binary search tree


1. Verify the preorder sequence of the binary search tree

255. Verification of Preorder Traversal Sequence Binary Search Tree - LeetCode (leetcode-cn.com)

Topic description:

Problem solving ideas:

  • The nature of BST is that the left subtree is smaller than the root, and the right subtree is larger than the root, so check this property.
  • First find the value with the larger start position of the array, that is, find the value larger than the root node. If the given sequence is the preorder sequence of the binary search tree, then the subsequent sequences are all the right subtrees of the root, and the following sequence must be satisfied. The tree has nodes larger than the root. If it is not satisfied, it is definitely not a binary search tree. If it is satisfied, we will divide the whole sequence into two parts, the recursive sub-subtree part and the right sub-tree part. If these two parts are satisfied, then this sequence is the preorder sequence of the binary search tree.

Corresponding code:

class Solution
{
public:
    bool verifyPreorder(vector<int> &preorder)
    {
        return checkBSTPreorder(preorder, 0, preorder.size() - 1);
    }
    bool checkBSTPreorder(vector<int> &preorder, int begin, int end)
    {
        if (begin >= end)
        {
            return true;
        }
        int index = begin;
        while (index <= end && preorder[index] <= preorder[begin]) //找到第一个小于preorder[left]
        //的数
        {
            index++;
        }
        //检查后面是否有小于根节点值如果有说明不是二叉搜索树的前序序列
        int tmp = index;
        while (tmp <= end)
        {
            if (preorder[tmp] <= preorder[begin])
            {
                return false;
            }
            tmp++;
        }
        //检查左子树和右子树是不是
        return checkBSTPreorder(preorder, begin + 1, index - 1) &&
               checkBSTPreorder(preorder, index, end);
    }
};

But unfortunately the time complexity is too high to time out. Here is another method

Method 2: Monotonic Stack

The binary search tree is left < root < right, and the pre-order traversal is root->left->right. Based on such properties and traversal methods, we know that the farther to the left, the smaller it is. In this way, a monotonically decreasing tree can be constructed. The stack to keep track of the elements traversed.

Why use a monotonic stack? Because in the process of traversing the left subtree, the value is getting smaller and smaller. Once the value is greater than the value of the top element of the stack, it means that the right subtree will start to enter (if not, then You should continue to enter the left subtree. If you don't understand, please see the definition of binary search tree), but which node does this right subtree start from?

The monotonic stack helps us record these nodes. As long as the top element of the stack is smaller than the current node, it means that it is still a left subtree and needs to be removed, because we need to find the parent node directly connected to the right child node, that is, to find this subtree As long as the top element of the stack is still smaller than the current node, it will be popped until the top element of the stack is greater than the node, or the stack is empty. The previous element at the top of the stack is the root of the subtree node.

Next, the array continues to traverse backward, and each node of the right subtree must be larger than the root of the subtree to satisfy, otherwise it is not a binary search tree.

Corresponding code:

class Solution {
public:
    bool verifyPreorder(vector<int>& preorder) {
         int Min=INT_MIN;//最小值
         stack<int>stk;
          for(int i=0;i<preorder.size();i++)
          {
              // 右子树元素必须要大于递减栈被top访问的元素,否则就不是二叉搜索树
              if(preorder[i]<Min)//不满足搜索二叉树的性质
              {
                  
                  return false;
              }
              while(!stk.empty()&&preorder[i]>stk.top())
              {
              // 数组元素大于单调栈的元素了,表示往右子树走了,记录下上个根节点
         // 找到这个右子树对应的根节点,之前左子树全部弹出,不在记录,因为不可能在往根节点的左子树走了
                  Min=stk.top();//更新最小值
                  stk.pop();
              }
              stk.push(preorder[i]);//入栈;
          }
          return true;//说明是二叉搜索树的前序遍历
    }
};

2. Preorder traversal to restore binary search tree

1008. Preorder Traversal Constructing Binary Search Tree - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

The title gives us the preorder traversal of the binary search tree, so there is no doubt that the first element is the root node of the entire tree. The binary search has the following properties:

1. All nodes of the left subtree have values ​​less than the root node.

2. All nodes of the right subtree have values ​​greater than the root node.

So we can traverse this array to find the first element larger than the root node, then from this element onwards are the right subtree part. In this way, we can divide the array into [left subtree] root [right subtree]. The left subtree and right subtree can be constructed recursively. Take [8, 5, 1, 7, 10, 12] as an example, 8 is the root node, [5, 1, 7] smaller than 8 is the value on his left subtree, and larger than him [10, 12] ] is the value on his right subtree. So you can refer to the method of binary search to divide the array into two parts, he is like this:

Then we split [5, 1, 7] on the left according to the above method. 5 is the root node, 1 smaller than 5 is the left child node, and 7 larger than 5 is the right child node. Similarly, 10 in [10, 12] on the right is the root node, and 12 larger than 10 is the right child node, so we keep splitting until we can't split, so the result is as follows:

Corresponding code:


class Solution {
public:
    TreeNode* bstFromPreorder(vector<int>& preorder) {
         return buildTree(preorder,0,preorder.size()-1);
    }
    TreeNode*buildTree(vector<int>&preorder,int left,int right)
    {
        if(left>right)
         return nullptr;//区间不存在说明没有数了
         int index=left;//从开始位置查找第一个比preorder[left]大的数
         //将其划分为[left+1,index-1]左子树[left]根[inde,right]//右子树
         while(index<=right&&preorder[index]<=preorder[left])
        {
            index++;
        }
        //[left+1,index-1]左子树[left]根[inde,right]
        TreeNode*root=new TreeNode(preorder[left]);//根节点
        root->left=buildTree(preorder,left+1,index-1);//构建左子树
        root->right=buildTree(preorder,index,right);//构建右子树
        return root;
    }
};

Can this problem be optimized? We find that every time we have to find the element on the right that is larger than him and closest to him, the time complexity is O(N^2). And isn't that exactly what monotonic stacks do? So we can use the monotonic stack to preprocess and optimize out this traversal operation in advance. Let the time replication degree be reduced to O(N). Corresponding code

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* bstFromPreorder(vector<int>& preorder) {
        int N=preorder.size();
        vector<int>nearBig(N,-1);//用于存储一个元素右边比他大并且离他最近位置的小标
        //全部先设置为-1是为了提前解决结算阶段,栈中元素右边没有比他大的了
         stack<int>stk;
         for(int i=0;i<N;i++)
         {
             while(!stk.empty()&&preorder[stk.top()]<preorder[i])
             {
                  nearBig[stk.top()]=i;
                  stk.pop();
                  
             }
             stk.push(i);
         }
      
         return buildTree(preorder,0,preorder.size()-1,nearBig);
    }
    TreeNode*buildTree(vector<int>&preorder,int left,int right,vector<int>&nearBig)
    {
        if(left>right)
         return nullptr;//区间不存在说明没有数了
        
         //将其划分为[left+1,index-1]左子树[left]根[inde,right]//右子树
        /* while(index<=right&&preorder[index]<=preorder[left])
        {
            index++;
        }*/
        int index=nearBig[left]==-1?right+1:nearBig[left];

        //[left+1,index-1]左子树[left]根[inde,right]
        TreeNode*root=new TreeNode(preorder[left]);//根节点
        root->left=buildTree(preorder,left+1,index-1,nearBig);//构建左子树
        root->right=buildTree(preorder,index,right,nearBig);//构建右子树
        return root;
    }
};

3. Post-order traversal of binary search tree

Sword refers to Offer 33. Post-order traversal sequence of binary search tree - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

The idea of ​​solving this problem is basically the same as the above problem. First find the root node, then find the first number smaller than the root node from right to left, and then divide it into a left subtree and a right subtree. In judging the left subtree Is there a number larger than the root node? If there is a description that it is not a post-order traversal of the binary search tree, if both are smaller than the root node, recurse the left subtree and the right subtree. The left subtree and the right subtree are satisfied at the same time. Take [3, 5, 4, 10, 12, 9] as an example below:

We know that the last number in the subsequent traversal must be the root node, so the last number 9 in the array is the root node. We find the first number 10 larger than 9 from front to back, then [10, 12] after 10 (except 9) All are the right child nodes of 9, and [3, 5, 4] in front of 10 are all left child nodes of 9. The latter needs to be judged. If there is less than 9, it means that it is not a binary search tree, and returns false directly. . Then, the left and right subtrees are judged recursively.

Let's look at another one, his subsequent traversal is [3, 5, 13, 10, 12, 9].

Let's split according to the array, the first one larger than 9 is followed by the right child of 9 [13, 10, 12]. Then split the array, 12 is the root node, the first one larger than 12 is followed by the right child node [13, 10] of 12, but we see that 10 is smaller than 12, it cannot be 12 right child, so we can be sure that this tree is not a binary search tree. After understanding the above principle, let's look at the code.

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
         return checkBSTPost(postorder,0,postorder.size()-1);
    }
    bool checkBSTPost(vector<int>&nums,int left,int right)
    {
        if(left>=right)
         return true;
         int index=right;//找到第一个比根节点小的树那么前面的及它自己都是左子树部分
         while(index>=left&&nums[index]>=nums[right])
         {
             index--;
         }
        int tmp=left;//检查前面的左子树部分是否有大于根节点
        while(tmp<index)
        {
            if(nums[tmp]>nums[right])
            {
                return false;
            }
            tmp++;
        }
          //递归检查左子树和右子树
        return checkBSTPost(nums,left,index)&&checkBSTPost(nums,index+1,right-1);

    }
};

Solution 2: Monotonic Stack

We all know that postorder traversal is left and right root if we reverse it to see if it becomes root right left. At this point we can use a monotonic stack (incrementing on the right).

Observe the structure of the binary search tree, check the post-order traversal in reverse order, and post-order traversal in reverse order: [root node | right subtree | left subtree]
From the root node to the right subtree to the right subtree of the right subtree. .. is an increasing sequence, such as 6, 8, 9.
When it is no longer incremented, it means that the left subtree node of a subtree appears, such as 7.
We put the continuously increasing nodes into a stack, and when there is no longer When increasing the number of nodes, the nodes in the stack are popped one by one, until there is no node larger than it in the stack.
Then the last node popped from the stack is its parent node, and the value of the parent node is continuously updated. Each time it is judged whether the current node is greater than the value of the parent node, if it is greater than it is definitely wrong.

Corresponding code:

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
          int Max=INT_MAX;
          stack<int>stk;
          for(int i=postorder.size()-1;i>=0;i--)
          {
              if(postorder[i]>Max)
              {
                  return false;
              }
              while(!stk.empty()&&postorder[i]<stk.top())
              {
                  Max=stk.top();
                  stk.pop();
              }
              stk.push(postorder[i]);
          }
          return true;
    }
};

Guess you like

Origin blog.csdn.net/qq_56999918/article/details/123979032