单调队列和单调栈(通俗易懂)

单调队列

单调队列顾名思义就是具有单调性的队列,其中单调性可以单调递增也可以单调递减,并且。队首和队尾可以进行出队操作,队尾可以进行入队操作。队首元素维护的是区间的最大值或最小值
单调队列:可以头删(过期),尾删(淘汰),尾插
单调队列的形象比喻

假如某高校ACM校队每年只有一个名额去参加比赛,所以只能选出能力最强的人去参加,因为该校只有大三及其以下能够参加该比赛,所以超过该限制的人将会被淘汰。现该队有三名队员分别是张三(年级:大三,能力:9)李四(年级:大二,能力:8)王五(年级:大一,能力:6)。在这一年,因为张三能力最强所以只有张三能够参加比赛。过了一年后,张三退役(也就是队首出队的操作),现在队里成员信息是李四(年级:大三,能力:8)王五(年级:大二,能力:6)两人,这时候来了一个小萌新(队尾入队),他的信息是刘六(年级:大一,能力:10)。因为刘六的能力比李四王五强,所以在接下来不加新人的情况下,一直会是刘六参加比赛,李四王五的大学生涯不会参见比赛了(李四王五队尾出队)。

滑动窗口

题目描述
​ 给出一个长度为 𝑁 的数组,一个长为 𝐾 的滑动窗口从最左移动到最右,每次窗口移动,如下图:
在这里插入图片描述
找出窗口在各个位置时的极大值和极小值。

输入
​ 第一行两个数 𝑁,𝐾。
​ 第二行有 𝑁 个数,表示数组中的元素。

输出
​ 输出两行,第一行为窗口在各个位置时的极小值,第二行为窗口在各个位置时的极大值。

样例输入
8 3
1 3 -1 -3 5 3 6 7

样例输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7

数据规模与约定
​ 时间限制:1 s
​ 内存限制:256 M
​ 100% 的数据保证 1≤𝑁≤300000

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
using namespace std;
#define MAX_N 300000
int a[MAX_N + 5];
int q[MAX_N + 5], head = 0, tail = 0;

int main() {
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        while (tail - head && a[q[tail - 1]] >= a[i]) tail--;  //队尾出队
        q[tail++] = i;                            //队尾入队
        if (q[head] <= i - k) head++;  //队首入队
        if (i < k) continue;
        i == k || cout << " ";
        cout << a[q[head]];
    }
    cout << endl;
    head = tail = 0;
    for (int i = 1; i <= n; i++) {
        while (tail - head && a[q[tail - 1]] <= a[i]) tail--;
        q[tail++] = i;
        if (q[head] <= i - k) head++;
        if (i < k) continue;
        i == k || cout << " ";
        cout << a[q[head]];
    }
    cout << endl;
    return 0;
}

239. 滑动窗口最大值

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> q(nums.size(),0);
        vector<int> vec;
        int tail = 0, head = 0;
        for(int i = 0;i< nums.size();i++){
        //淘汰操作
            while(tail - head && nums[i] > nums[q[tail-1]]) tail--;
            //尾插操作
            q[tail++] = i;
            //过期头删操作
            if(i - q[head] >= k) head++;
            if(i < k-1) continue;
            vec.push_back(nums[q[head]]);
        }
        return vec;
    }
};

洛谷P1714 切蛋糕
该问题涉及前缀和以及单调队列问题

#include<iostream>

using namespace std;
int sum[500000],q[500000];
int main(){
    int n,m,ans;
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> sum[i];
        sum[i] += sum[i-1];
    }
    ans = sum[1];
    int tail = 0, head = 0;
   // q[tail++] = 0;
    for(int i = 1; i <= n; i++){
        ans = max(ans,sum[i] - sum[q[head]]);
        while(tail - head && sum[i] <= sum[q[tail-1]]) tail--;
        q[tail++] = i;
        if(i - q[head] >= m) head++;
    }
    cout << ans << endl;
    return 0;
}

单调栈

单调栈就是从数组中找到左右两边比你大的数或者比你小的数而且时间复杂度为O(N)
单调栈与单调队列的唯一区别就是单调栈没有头删操作(过期)
单调栈与单调队列性质类似通俗的说就是具有单调性的栈,其中单调性可以单调递增也可以单调递减,并且,队尾可以进行出队操作,队尾可以进行入队操作。
单调递增栈就是从栈底到栈顶是从小到大
单调递减栈就是从栈底到栈顶是从大到小
单调队列:擅长维护区间最大/最小值,最小值对应着递增队列,最大值对应着递减队列
单调栈::擅长维护最近大于/小于关系,从左侧先入栈就是维护左侧最近关系,从右侧先入栈就是维护右侧最近关系
单调栈模板
寻找最近大于关系维护单调递减栈

stack<int> s;
for (遍历这个数组)
{
		//当判断是栈顶元素小于当前元素为单调递减栈,相反为单调递增栈
		while (栈不为空 && 栈顶元素小于当前元素)
		{
			栈顶元素出栈;
			更新结果;
		}
		当前数据入栈;
}

739. 每日温度

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        stack<int> s;
        vector<int> v(T.size(),0);
        for(int i = 0; i < T.size(); i++) {
            while(!s.empty() &&  T[s.top()] < T[i]){
                v[s.top()] = i-s.top();
                s.pop();
            }
            s.push(i);
        }
        return v;
    }
};

496. 下一个更大元素 I

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res;
        unordered_map<int, int> mp;
        stack<int> sk;
        for(int n: nums2){
        //因为要找下一个比它大的元素,要维护单调递增栈
            while(!sk.empty() && sk.top() < n){
                mp[sk.top()] = n;
                sk.pop();
            }
            sk.push(n);
        }
        while(!sk.empty()){
            mp[sk.top()] = -1;
            sk.pop();
        }
        for(int n: nums1){
            res.push_back(mp[n]);
        }
        return res;        
    }
};

503. 下一个更大元素 II

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        stack<int> s;
        vector<int> v(nums.size(),-1);
        unordered_map<int ,int> mp;
        if(nums.size() == 0) {
            return v;
        }
        for(int i = 0; i <= 2*nums.size()-1; i++){
            while(!s.empty() && nums[s.top()] < nums[i%nums.size()]){
                v[s.top()] = nums[i%nums.size()];
                s.pop();
            }
            s.push(i%nums.size());
        }
        /* for(int i = 0; i < nums.size(); i++){
            v.push_back(mp[i]);
        }*/
        return v;
    }
};

739. 每日温度

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        stack<int> s;
        vector<int> v(T.size(),0);
        for(int i = 0; i < T.size(); i++) {
            while(!s.empty() &&  T[s.top()] < T[i]){
                v[s.top()] = i-s.top();
                s.pop();
            }
            s.push(i);
        }
        return v;
    }
};

最大矩形面积

题目描述
​ 给定从左到右多个矩形,已知这此矩形的宽度都为 1,长度不完全相等。这些矩形相连排成一排,求在这些矩形包括的范围内能得到的面积最大的矩形,打印出该面积。所求矩形可以横跨多个矩形,但不能超出原有矩形所确定的范围。
在这里插入图片描述

输入
​ 输入共一行,第一个数表示矩形的个数 𝑁。接下来 𝑁 个数表示矩形的大小。(1≤𝑁≤100000)

输出
​ 输出最大矩形面积。

样例输入
7
2 1 4 5 1 3 3

样例输出
8

数据规模与约定
​ 时间限制:1 s
​ 内存限制:256 M
​ 100% 的数据保证 1≤𝑁≤100000

#include<iostream>

using namespace std;
#define max_n 100000
long long num[max_n + 5];
int l[max_n + 5];
int r[max_n + 5];
int q[max_n + 5];
int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> num[i];
    }
    int tail;
    num[0] = -1;
    q[tail = 0] = 0;
    for(int i = 1; i <= n; i++){
        while(num[i] <= num[q[tail]]) tail--;
        l[i] = q[tail];
        q[++tail] = i;
    } 
    num[n + 1] = -1;
     q[tail = 0] = n+1;
    for(int i = n; i >= 1; i--){
        while(num[i] <= num[q[tail]]) tail--;
        r[i] = q[tail];
        q[++tail] = i;
    } 
    long long ans = 0;
    for(int i = 1; i <= n; i++){
        ans = max(ans,num[i] * (r[i] - l[i] - 1 ));
    }
    cout << ans << endl;
    return 0;
}

372. 双生序列

#include<iostream>

using namespace std;
#define max_n 500000
int a[max_n + 5],b[max_n + 5];
int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }
    for(int i = 1; i <= n; i++){
        cin >> b[i];
    }
    int top1 = -1,top2 = -1,p;
    for( p = 1; p <= n; p++){
        while(top1 != -1 && a[p] <= a[top1]) top1--;
        while(top2 != -1 && b[p] <= b[top2]) top2--;
        if(top1 - top2) break;
        a[++top1] = a[p];
        b[++top2] = b[p];
    }
    cout << p - 1 << endl;
    return 0;
}

参考:https://blog.csdn.net/lucky52529/article/details/89155694

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中,单调单调队列是两种不同的数据结构。单调是一个,它的特点是内的元素是单调的,可以是递增或递减的。在构建单调时,元素的插入和弹出都是在的一端进行的。与此类似,单调队列也是一个队列,它的特点是队列内的元素是单调的,可以是递增或递减的。在构建单调队列时,元素的插入是在队列的一端进行的,而弹出则是选择队列头进行的。 单调队列在解决某些问题时,能够提升效率。例如,滑动窗口最大值问题可以通过使用单调队列来解决。单调队列的结构可以通过以下代码来实现: ```python class MQueue: def __init__(self): self.queue = [] def push(self, value): while self.queue and self.queue[-1 < value: self.queue.pop(-1) self.queue.append(value) def pop(self): if self.queue: return self.queue.pop(0) ``` 上述代码定义了一个名为MQueue的类,它包含一个列表作为队列的存储结构。该类有两个方法,push和pop。push方法用于向队列中插入元素,它会删除队列尾部小于插入元素的所有元素,并将插入元素添加到队列尾部。pop方法用于弹出队列的头部元素。 总结来说,单调单调队列都是为了解决特定问题而设计的数据结构。单调在构建时元素的插入和弹出都是在的一端进行的,而单调队列则是在队列的一端进行的。在Python中,可以通过自定义类来实现单调队列的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值