Monotone queue and monotone stack

Posted by ricroma on Sat, 05 Feb 2022 10:32:20 +0100

1, Monotone queue

1. Monotone queue introduction

Monotone queue refers to a data structure in which the elements in a queue have strict monotonicity. It is divided into monotone increasing queue and monotone decreasing queue.
Monotone queues satisfy two properties:
(1) The monotone queue must satisfy the strict monotonicity from the head to the tail of the queue;
(2) Those in front of the queue are more advanced than those behind the queue; The monotonic queue problem is also known as the "sliding window" problem. It maintains a monotonic window. The element entering the queue is to compare the element with the element at the end of the queue. When the maintained window is monotonically increasing, if the changed element is greater than the element at the end of the queue, the element is directly thrown into the queue until it is satisfied that the element is greater than the element at the end of the queue.

Title: given an array with length N and a sliding window with size M (0 < m < = n), please find the maximum value in all East China windows.

For example, in the array {2, 6, 4, 9, 8, 5, 5, 2}, if the size of the sliding window is 3, there are 6 sliding windows, and their maximum values are 6, 9, 9, 8 and 5 respectively
Analysis: 6 sliding windows and their maximum values: {[2,6,4], 9,8,5,5,2}, 6; {2,[6,4,9],8,5,5,2},9; {2,6,[4,9,8],5,5,2},9; {2,6,4,[9,8,5],5,2},9; {2,6,4,9,[8,5,5],2},8; {2,6,4,9,8,[5,5,2]},5; 1, Simple approach: (calculating the maximum value of the window requires O(m), moving n-m+1 times, and the time complexity is O(nm).

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int m = 3, n = 8;
    // cin >> m >> n;
    int a[] = {0,2,6,4,9,8,5,5,2};
    // for (int i = 1; i <= n; i++)
    // {
    //     cin >> a[i];
    // }
    for (int i = m; i <= n; i++)
    {
        int mx = a[i];
        for (int j = i - m + 1; j <= i; j++)
        {
            mx = max(mx, a[j]);
        }
        cout << mx << " ";
    }

2. Monotone queue implementation

Monotonic queue: the elements in the queue are monotonic, increasing or decreasing. The monotone queue is used to store the monotonically decreasing elements of the current window, and the queue head is the maximum value in the window, and the queue tail is the tail element in the window. That is, the queue is a subsequence of elements from the maximum value to the tail in the corresponding window from the head to the tail of the queue.
1. Header element: when the header element slides out of the window, the header element will exit the queue.
2. Join the team at the end of the team: when a new element slides into the window, it is necessary to insert the new element from the end of the team, which can be divided into two cases:
(1) Direct insertion: if the new element is smaller than the end of the queue element, it is inserted directly from the end of the queue, because it may be called the maximum value when the previous maximum value slides out of the window;
(2) Delete before insert: if the new element is greater than or equal to the end of the queue element, delete the end of the queue element (because it can not become the maximum value in the window), and delete repeatedly until the queue is empty or a value greater than the new element is encountered, and insert it later. In this way, the maximum value of the window can be taken from the team head each time.
⭐⭐ Note: the queue should store the subscript value of the window element to make it easy to judge whether the queue head is out of the queue.
Array simulation queue

#include<bits/stdc++.h>
using namespace std;
int main(){
    int h = 0,t = -1;int n,m;
    cin >> n >> m;
    int a[n],q[n];//q[n] array simulates queue and stores element subscripts
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }
    for(int i = 1; i <= n; i++){
        //q[h] is not in the window [i - m + 1,i], the team leader leaves the team
        if(h <= t && q[h] < i - m + 1){
            h++;
        }
        //Current value ≥ end of queue value, end of queue out of queue
        while(h <= t && a[i] >= a[q[t]]) t--;
        //Subscript into the team, so that the team head can get out of the team
        q[++t] = i;
        //Use queue head maximum
        if(i > m - 1) cout << a[q[h]] <<" ";
    }}

STL implementation

#include<bits/stdc++.h>
#include<deque>
using namespace std;
deque<int> q; 
int main(){
    int n,m;
    cin >> n >>m;
    int a[n];
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }
    for(int i = 1; i <= n; i++){
        //Judge whether the team leader needs to leave the team
        if(!q.empty() && q.front() < i - m + 1){
            q.pop_front();
        }
        //Maintain queue monotonicity
        while(!q.empty() && a[i] > a[q.back()]){
            q.pop_back();
        }
        //Subscript into the team, so that the team head can get out of the team
        q.push_back(i);
        //Take the queue header element as the maximum value of the window
        if(i > m - 1) cout << a[q.front()] <<" ";

    }
}

Example: [template] monotonic queue
The title description has a sequence aa with a length of nn and a window with a size of kk. Now slide from the left to the right, one unit at a time, and find the maximum and minimum values in the window after each slide.

For example:

The array is [1,3,-1,-3,5,3,6,7][1,3,−1,−3,5,3,6,7], and k=3.

Input format
There are two lines of input. The first line has two positive integers n,kn,k. The second line contains nn integers, representing the sequence aa
Output format
There are two lines of output. The first line is the minimum value of each window sliding
The second line is the maximum value of each window sliding
Sample input and output
input
8 3
1 3 -1 -3 5 3 6 7
output
-1 -3 -3 -3 3 3
3 3 5 5 6 7

2, Monotone stack

1. Definition:

Monotonically increasing stack: the elements in the stack are increasing from the bottom of the stack to the top of the stack.
Monotonically decreasing stack: the elements in the stack are decreasing from the bottom of the stack to the top of the stack.

2. Application:

Solve the next position greater than or less than x element.

For an array, return an array of the same size. The value of the ith position of the returned array should be, for the ith element in the original array, at least how many steps to the right can you encounter an element larger than yourself (if there is no element larger than yourself or it is the last element, put - 1 at the position of the returned array)

#include <iostream>
#include <stack>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int num[100] = {1, 3, 4, 5, 2, 9, 6};
int res[100];
struct info
{
    int no;
    int data;
};
stack<info> s;
void nextGreater(int a[], int n)
{
    for (int i = n - 1; i >= 0; i--)
    {
        while (!s.empty() && s.top().data <= a[i])
        {
            s.pop();
        }
        res[i] = s.empty() ? -1 : s.top().no - i;
        info temp;
        temp.no = i;
        temp.data = num[i];
        s.push(temp);
    }
}
 
int main()
{
    nextGreater(num, 7);
    for (int i = 0; i < 7; i++)
    {
        cout << res[i] << " ";
    }
}

Now find the value of the first element larger than him

#include<iostream>
#include<cmath>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn = 1e6 + 10;
int res[maxn];
int num[maxn];
stack<int> s;
void nextGreater(int a[],int n){
    for(int i = n; i >= 1; i --){
        while(!s.empty() && a[i] >= s.top()){
            s.pop();
        }
        res[i] = s.empty()?-1:s.top();
        s.push(a[i]);
    }
}
int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> num[i];
    }
    nextGreater(num,n);
    for(int i = 1; i <= n; i++){
        cout << res[i] <<" ";
    }
     
}

How many steps away from it

#include <iostream>
#include <algorithm>
#include <cmath>
#include <stack>
using namespace std;
const int n = 1e6 + 10;
int res[n];
int num[n];
struct info
{
    int no;
    int data;
};
stack<info> s;
void nextGreaterid(int a[], int n)
{
    for (int i = n; i >= 1; i--)
    {
        while (!s.empty() && a[i] >= s.top().data)
        {
            s.pop();
        }
        res[i] = s.empty() ? -1 : s.top().no - i;
        info temp;
        temp.no = i;
        temp.data = num[i];
        s.push(temp);
    }
}
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> num[i];
    }
    nextGreaterid(num, n);
    for (int i = 1; i <= n; i++)
    {
        cout << res[i] << " ";
    }
}

Topics: Algorithm data structure