Take your hand to tear the stack

Posted by mikosiko on Mon, 17 Jan 2022 03:16:09 +0100

Monotone stack

Is it time to use monotone stack

We must remember that the data structure must assist us in solving problems, and must be optimized in the way of violent solution (in fact, space for time)

The monotone stack must meet the first in first out feature when processing data, just as we must meet the first in first out feature when using priority queue

How to make good use of monotone stack

There are several problems to understand when using monotone stack well

  • What is the specific meaning of stacking
  • What is the specific meaning of out of stack
  • When does it get into the stack
  • When will it come out
  • The order of entering the stack: remember that the stack is entering the stack, backward out of the stack, backward in the stack, and positive out of the stack
  • How to set up sentry points

Template

vector<int> answer(vector<int>& nums) {
    vector<int> ans(nums.size());
    stack<int> st;
    for(int i=nums.size()-1;i>=0;i--) {
        while(!st.empty() && st.top()<=nums[i]) { // The second condition here should be changed according to the actual situation
            st.pop();
        }
        ans[i]=st.empty()? 0 : st.top();
        st.push(nums[i])
    }
    return ans;
}

In fact, it can also be traversed, or sentinel points can be added to better solve the problem

That is, for() iterates back and forth. Because we use the stack structure, we are actually going out of the stack by going into the stack backwards

Examples

739. Daily temperature

Medium difficulty 817

According to the daily temperature list, please calculate how many days you need to wait for a higher temperature each day. If the temperature will not rise after that, please replace it with 0 in this position.

Example 1:

input: temperatures = [73,74,75,71,69,72,76,73]
output: [1,1,4,2,1,1,0,0]

Example 2:

input: temperatures = [30,40,50,60]
output: [1,1,1,0]

Example 3:

input: temperatures = [30,60,90]
output: [1,1,0]

Tips:

  • 1 <= temperatures.length <= 105
  • 30 <= temperatures[i] <= 100

Don't panic when you get the problem. Conduct a wave of analysis first. Any problem has certain characteristics. When you get the problem, you must first think about how to solve it, and then find out the characteristics of the problem for optimization

Violent solution to the problem:
	Is that each number traverses back to find the time complexity is o(n*n)
	At this time, why do you look back every time, because you don't remember what values are bigger than him
	At this time, only 73 can know the solution of the problem-----No
	73,74 And-------Yes, the answer at position 73 is 1, but you don't know the answer at position 74
	This reflects a characteristic
	Visit the points first and then get the results.
	This is very consistent with the processing characteristics of the stack, so we use the monotonic stack to solve it

positive sequence

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        // Positive order traversal problem solving

        vector<int> ans(temperatures.size(),0);
        stack<int> st;
        for(int i=0;i<temperatures.size();i++) {
            while(!st.empty() && temperatures[i]>temperatures[st.top()]) {
                ans[st.top()]=i-st.top();
                st.pop();
            }

            st.push(i);
        }
        return ans;
        

    }
};

Reverse order + sentry

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        // thinking
        /* 
            This should be traversed from the back to the front, because the previous results depend on the back, and the latter results are easier to get
            After that, we can see that this is consistent with the processing method of the stack. We first traverse the data and then get the result
        
         */

        vector<int> res(temperatures.size()+1);
        stack<int> st;
        temperatures.push_back(0);
        for(int i=temperatures.size()-1;i>=0;i--) {
            while(!st.empty() && temperatures[i]>=temperatures[st.top()]) {
                st.pop();
            }

            res[i]=st.empty()? 0 : st.top()-i;
            st.push(i);

        }
        res.pop_back();
        return res;


    }
};

496. Next larger element I

Simple difficulty 452

Give you two arrays nums1 and nums2 without duplicate elements, where nums1 is a subset of nums2.

Please find out the next larger value of each element in nums1 in nums2.

The next larger element of the number x in nums1 refers to the first element larger than x to the right of the corresponding position in nums2. If it does not exist, the corresponding position outputs - 1.

Example 1:

input: nums1 = [4,1,2], nums2 = [1,3,4,2].
output: [-1,3,-1]
explain:
    about num1 You can't find the next larger number in the second array, so output -1 . 
    about num1 The number 1 in the second array, and the next larger number to the right of the number 1 in the second array is 3.
    about num1 The number 2 in the second array has no next larger number, so it is output -1 . 

Example 2:

input: nums1 = [2,4], nums2 = [1,2,3,4].
output: [3,-1]
explain:
    about num1 The number in the second array is 2, and the next larger number in the second array is 3.
    about num1 The number 4 in the second array has no next larger number, so it is output -1 . 

Preorder traversal

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        // Obviously, it is in line with the habit of first in and then out
        // Understand the meaning of the question. Nums1 is a subset of nums2, that is, the length of nums1 < = the length of nums2
        // You can use the monotone stack to calculate the maximum value of each number of nums2, and there are no duplicate elements
        map<int,int> mp; // [number, subscript]

        for(int i=0;i<nums2.size();i++) {
            mp[nums2[i]]=i;
        }

        vector<int> ans(nums2.size(),-1);
        stack<int> st;
        for(int i=0;i<nums2.size();i++) {

            while(!st.empty() && nums2[st.top()]<nums2[i]) {
                ans[st.top()]=nums2[i];
                st.pop();

            }
            st.push(i);

        }


        vector<int> res(nums1.size());
        for(int i=0;i<nums1.size();i++) {
            res[i]=ans[mp[nums1[i]]];
        }
        return res;

    }
};

Post traversal + sentry

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        // Obviously, it is in line with the habit of first in and then out
        // Understand the meaning of the question. Nums1 is a subset of nums2, that is, the length of nums1 < = the length of nums2
        // You can use the monotone stack to calculate the maximum value of each number of nums2, and there are no duplicate elements
        map<int,int> mp; // [number, subscript]

        for(int i=0;i<nums2.size();i++) {
            mp[nums2[i]]=i;
        }

        nums2.push_back(-1);
        vector<int> ans(nums2.size(),-1);
        stack<int> st;

        for(int i=nums2.size()-1;i>=0;i--) {
            while(!st.empty() && nums2[st.top()]<=nums2[i] ) {
                st.pop();
            }
            // At this time, the top of the stack smaller than the number is removed, which is a non strict monotonic stack
            ans[i]=st.empty() ? -1: nums2[st.top()];
            st.push(i);

        }
        ans.pop_back();
        vector<int> res(nums1.size());
        for(int i=0;i<nums1.size();i++) {
            res[i]=ans[mp[nums1[i]]];
        }
        return res;
    }
};

Topics: C++ Algorithm