Monotonic stack of data structures (including code implementation)

content

1. The basic concept of monotonic stack:

2. Application of monotonic stack

2.1 Monotonic stack

 2.2 Monotonic stack advanced

 2.3 Maximum rectangular area

 2.4 Maximum rectangle

2.5 Count the number of submatrices with all 1s


1. The basic concept of monotonic stack:

I believe everyone is very familiar with the stack? The stack has a very distinctive feature: first in, last out

The so-called monotonic stack is an additional feature added to the stack's FIFO basis: the elements from the top of the stack to the bottom of the stack are strictly increasing (or decrementing).

1. For a monotonically increasing stack, if the current push element is x, traverse the elements from the top of the stack, pop elements greater than or equal to x from the stack, and directly encounter an element less than x or the stack is empty, then push x into the stack middle.

2. For a monotonically decreasing stack, the elements smaller than x are popped each time.

Take monotonically increasing example:

In sequence: 3, 2, 4, 0, 1, 5

Let it be pushed onto the stack from left to right:

Step 1: Push 3 onto the stack.

Step 2: When 2 is pushed into the stack, it is found that the element in the stack is larger than itself, and the element 3 in the stack is popped out, and 2 is pushed into the stack.

Step 3: Push 4 into the stack and look at the top of the stack. The element at the top of the stack is smaller than me. Good 4, you can go to the stack.

Step 4: 0 into the stack Go to the top of the stack and see that you are actually bigger than me, okay, you can go out from the stack, 2, 4 are popped out of the stack and 0 is pushed into the stack

Step 5: 1. Go to the top of the stack and look at the top of the stack. The element at the top of the stack is smaller than me. Good 1. You can go to the stack.

Step 6: 5 Push to the top of the stack and look at the top of the stack. The element at the top of the stack is smaller than me. Good 5, you can go to the stack.

2. Application of monotonic stack

2.1 Monotonic stack

Monotonic stack structure

Topic description:

analyze:

1. To find the positions on the left and right of each i position in the array that are closest to i and smaller than the value of arr[i]. We can use a monotonic stack to achieve this: the subscript of the array is stored in the stack

And ensure that the elements corresponding to the subscripts of the elements from the bottom of the stack to the top of the stack are from small to large. If we want to find the position on the left and right of each i position in the array that is closest to the i position and whose value is greater than arr[i], then we It is necessary to keep the arr[i] corresponding to the subscript i from the top of the stack to the bottom of the stack from small to large.

Next, we use the monotonic stack to find the positions on the left and right of each i position in the array arr[3 4 1 5 6 2 7] that are closest to the i position and whose value is smaller than arr[i]:

1. When the array traverses to subscript 0, it is found that the stack is empty, and the subscript 0 can be directly pushed into the stack, as shown in the following figure:

insert image description here

 2. When the array traverses to subscript 1, it is found that element 3 at the top of the stack is smaller than me. Okay, you can push it into the stack, and 1 pushes it into the stack. As shown in the figure:

insert image description here

 3. Then when the array traverses to position 2, it is found that arr[2] is smaller than the top element of the stack, that is, the array element 4 corresponding to the subscript 1 of the array, then directly pops the top element 1 of the stack, then the left and right of the 1 position are away from the 1 position The most recent and value less than arr[1] are the new top 0 and the current i = 2, respectively, and are recorded in the result array. If there is no element closest to and smaller than arr[i] on the left and right of the current position i, then the default position is -1. Repeat the above process until the stack is empty or when arr[2] is greater than the array element corresponding to the subscript at the top of the stack, directly push i. The result of this process is shown in Fig.

insert image description here

 When the array is traversed from position 3 to position 4, the subscripts corresponding to array elements 5 and 6 are pushed onto the stack. As shown below:

insert image description here

 When the array is traversed to position 5, it is found that arr[5] is smaller than the array element 6 corresponding to element 4 in the top of the stack, then pop the top element of the stack and record the subscript of the nearest element whose left and right are smaller than arr[5]. Similarly, the new top of the stack will be popped. Then push the subscript 5 of element 2. As shown below:

insert image description here

 When the array traverses to the 6th position, it is found that arr[6] is greater than the arr[5] corresponding to the top element 5 of the stack, that is, 2, then directly press the subscript 6. Since then, the array traversal has ended, and the state of the stack and result array is shown in the following figure:

insert image description here

 Next, we pop the top element idx of the stack in a loop, and record the subscripts of the elements on the left and right of arr[idx] that are closest to and smaller than arr[idx], we can find the elements that are closest to the left and smaller than arr[idx] It is marked as the new top element of the stack (-1 if it does not exist), and the subscript of the element closest to the right and smaller than arr[idx] does not exist, that is, -1. Finally we get the following result array:

insert image description here

Corresponding code:

#include<iostream>
#include<stack>
#include<vector>
using namespace std;
int main(){
    int n;
    cin>>n;
    vector<int>arr(n);
    for(int i=0;i<n;i++){
        cin>>arr[i];
    }
      stack<int>stk;
    vector<vector<int>>ans(n,vector<int>(2));//保存答案
    for(int i=0;i<n;i++){
        while(!stk.empty()&&arr[stk.top()]>arr[i]){
              int cur=stk.top();
              stk.pop();
              int leftIndex=stk.empty()?-1:stk.top();//左边比它小的
            //右边比它小的就是当前的下标i
              ans[cur][0]=leftIndex;
              ans[cur][1]=i;
        }
        stk.push(i); 
    }
    while(!stk.empty()){
        int rightIndex=-1;//结算阶段右边一定没有比当前数要小的
        int cur=stk.top();//当前位置可以结算
        stk.pop();
        int leftIndex=stk.empty()?-1:stk.top();//看当前位置下面是否还压着数
        ans[cur][0]=leftIndex;
        ans[cur][1]=rightIndex;
    }
    //打印结果
    for(int i=0;i<n;i++){
        printf("%d %d\n",ans[i][0],ans[i][1]);
    }
    
}

 2.2 Monotonic stack advanced

Monotonic stack structure (advanced)

Topic description:

The difference between this question and the previous question is that there are repeated elements: then it is possible that the element on the stack is equal to the element on the current top of the stack, then the element on the right of the current top of the stack that is smaller than it cannot be correctly settled.

Therefore, at this time, we need to transform the structure of the monotone stack: changing the integer with subscripts in the stack to a linked list, the tonality remains unchanged. Take this set of data as an example: array arr[2,2,4,4,3]

1. When traversing to position 0 of the array, since the stack is empty, add the subscript 0 to the linked list, and then push the linked list onto the stack;

When traversing to bit 1 of the array, it is found that the stack is not empty, and because arr[1] is equal to arr[0] corresponding to element 0 of the linked list, that is, both are equal to 2, then subscript 1 is added to the top of the stack. In the linked list, as shown below:

insert image description here

2. When the array is traversed to No. 2, it is found that arr[2] is greater than arr[0] corresponding to element 0 of the linked list at the top of the stack, then directly add the subscript 2 to the new linked list, and then push the linked list into the stack. In addition, when the array is traversed to position 3, it is the same as when it is traversed to position 1 of the array, as shown below:

                                         

insert image description here

When the array is traversed to position 4, it is found that arr[4] is smaller than arr[2] corresponding to element 2 of the linked list at the top of the stack, then the linked list at the top of the stack is popped, and then the linked list is traversed. We can know that each item in the linked list The subscripts of the elements on the left and right sides of arr[idx] corresponding to the element idx that are closest to it and less than arr[idx] are the rightmost element of the new stack top linked list (here is 3) and the currently traversed array element. subscript (here 4). Then repeat the above process until arr[4] is less than or equal to arr[idx] corresponding to the new stack top linked list element idx.

If arr[4] is equal to arr[idx] corresponding to the new stack top linked list element idx, then add arr[4] to the stack top linked list, otherwise add arr[4] to the new linked list, Then add the linked list to the stack. Since arr[4] is not equal to arr[2] corresponding to element 2 of the linked list at the top of the stack, then we add arr[4] to the new linked list, and then add the linked list to the stack. as the picture shows:

insert image description here

 Next, we start to pop the linked list in the stack in a loop, and then traverse the linked list. We can know that the left side of the arr[4] corresponding to the linked list element 4 is the nearest and less than arr[4] The subscript of the element is the new stack The subscript 1 of the rightmost element of the top linked list, the right one does not exist, the default is -1.

insert image description here

We continue to pop the top-of-the-stack list and traverse the list. We found that there is no new stack top, then we indicate that there is no element on the left of arr[idx] corresponding to the current linked list element idx that is closest to it and smaller than arr[idx], our default subscript is -1, and the one on the right does not exist. , defaults to -1.

insert image description here

 Corresponding code:

#include<iostream>
#include<vector>
#include<stack>
#include<list>
using namespace std;
int main(){
   int n;
    cin>>n;
    vector<vector<int>>ans(n,vector<int>(2));//保存答案
    vector<int>arr(n);
    stack<list<int>>stk;//栈中存储链表
    for(int i=0;i<n;i++){
        cin>>arr[i];
        while(!stk.empty()&&arr[i]<arr[stk.top().front()]){
            //不满足单调性
                 list<int>tmp=stk.top();//当前节点可以结算了
                 stk.pop();
                int leftIndex=stk.empty()?-1:stk.top().back();//左边比他小的元素下标
               for(auto x:tmp){
                   ans[x][0]=leftIndex;
                   ans[x][1]=i;//右边比他小的下标对应的元素就是当前的i
               }
        }
        if(!stk.empty()&&arr[stk.top().front()]==arr[i]){
            //如果相等加入栈顶元素的链表中
            stk.top().push_back(i);
        }
        else{
            stk.push({i});
        }
    }
    
    while(!stk.empty()){//结算阶段右边比他小的没有了
        list<int>tmp=stk.top();
        stk.pop();
        int leftIndex=stk.empty()?-1:stk.top().back();
        for(auto x:tmp){
            ans[x][0]=leftIndex;
            ans[x][1]=-1;
        }
    }
    //输出结果
    for(int i=0;i<n;i++){
       printf("%d %d\n",ans[i][0],ans[i][1]);
    }
    
}

 2.3 Maximum rectangular area

The sword refers to Offer II 039. The maximum rectangular area of ​​the histogram - LeetCode (leetcode-cn.com)

Topic description:

 Problem solving ideas:

Solution to this problem:

Using the monotonic stack, find the area of ​​each column as the height of the rectangle, and take the largest.

That is, to find the coordinates (closest) of the two pillars on the left and right sides that are shorter than the pillar, the area is the width between the shorter pillars on both sides multiplied by the height of the pillar.

As shown in the figure, the nearest pillars on both sides of pillar B that are shorter than it are pillars A and C, then the area when pillar B is the height of the rectangle is

(C coordinate - A coordinate - 1) * B height

Special case:

1. If the left side of the desired column is not as short as it is, then when calculating the width, change the A coordinate of the above formula to -1 for calculation
2. When the right side of the desired column is not as short as it is, when calculating the width, use the above formula The C coordinate is changed to heights.length to calculate

Corresponding code:

class Solution {
public:
    int largestRectangleArea(vector<int>& arr) {
           stack<int>stk;
           int Maxans=0;//记录答案
           for(int i=0;i<arr.size();i++){
                 while(!stk.empty()&&arr[i]<=arr[stk.top()]){//等于
                     int cur=stk.top();
                     stk.pop();
                     int leftIndex=stk.empty()?-1:stk.top();//看自己底下是否压着东西
                     int curArea=(i-leftIndex-1)*arr[cur];
                     maxans=max(maxans,curArea);//看是否需要更新答案
                 }

                 stk.push(i);
           }
           //结算阶段
            while(!stk.empty()){
                int j=stk.top();
                stk.pop();
            int leftIndex=stk.empty()?-1:stk.top();
            
              int curArea=(arr.size()-leftIndex-1)*arr[j];
                 //当前的面积
                 maxans=max(maxAns,curArea);
            }
            return maxans;
    }
};

Note that if an equal element is encountered here, it can be popped from the stack, although the result of the popup is not correct, but an identical one comes in as long as it is calculated correctly.

 2.4 Maximum rectangle

85. Largest Rectangle - LeetCode (leetcode-cn.com)

 Topic description:

 analyze:

Violent enumeration: enumerate each upper left corner and each lower right corner, the complexity is O(n^2)*O(n^2), and then enumerate whether there is 0 in this area, the complexity is O(n^ 2), the total time complexity is O(n^6). can't pass

Method two monotonic stack:

We can enumerate: if the submatrix must be based on row 0, that submatrix contains the most 1s, and if the enumerated submatrix must be based on row 1, that submatrix contains the most 1s, repeat the above The answer to the process must be in it

2.5 Count the number of submatrices with all 1s

1504. Statistics of All 1 Subrectangles - LeetCode (leetcode-cn.com)

 

Problem solving ideas:

Referring to the solution of the previous question, the number of sub-matrices that are all 1 when the 0th row must be used as the foundation, and the number of sub-matrices that are all 1 when the 1st row must be used as the foundation. Repeat the above process and finally do not Did you ask for it? The technique used is the same space compression as the above question.

Suppose we compress it into such a histogram. We require the number of its sub-matrixes to be all 1s. First, for b, how many are the left and right smaller than it? Assuming that the length between 5 and 6 is n, there are a total of (n*(n+1)/2)*(5-2). Why not subtract 1? That's because he has calculated it before.

Corresponding code:

class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
            
                 int res=0;
                 vector<int>height(mat[0].size());
                 for(int i=0;i<mat.size();i++){
                     for(int j=0;j<mat[0].size();j++){//枚举第i行做地基子矩阵组中全为1的个数
                         height[j]=mat[i][j]==0?0:height[j]+1;
                     }
                     res+=countFromBottom(height);
                 }
                 return res;
    }
    int countFromBottom(vector<int>&height){
        int nums=0;
        stack<int>stk;
        for(int i=0;i<height.size();i++){
            while(!stk.empty()&&height[stk.top()]>=height[i]){
                int cur=stk.top();
                stk.pop();
                if(height[cur]>height[i]){//如果和你相等后面再算法
                    int leftIndex=stk.empty()?-1:stk.top();
                    int n=i-leftIndex-1;
                    int down=max(leftIndex==-1?0:height[leftIndex],height[i]);//取两者中最小的一个后面同一算
                    //最小的那个的时候统一算
                    nums+=num(n)*(height[cur]-down);//总的方法
                }
            }
            stk.push(i);
        }
          while(!stk.empty()){
              int cur=stk.top();
              stk.pop();
              int leftIndex=stk.empty()?-1:stk.top();
              int n=height.size()-leftIndex-1;
              int down=leftIndex==-1?0:height[leftIndex];
              nums+=(height[cur]-down)*num(n);

          }
          return nums;

    }

    int num(int n){//根据数学方法计算 1+2+....n
        return (n*(n+1))>>1;
    }
};

Guess you like

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