单调栈与单调队列

单调栈

特点

栈内的元素单调递增或者单调递减,可以在\(O(n)\)的时间内求出数列中所有数的左边或右边第一个比其大或小的元素,总时间复杂度为\(O(n)\)

例子

单调栈中一般存索引
一个单调递增栈s = [0, 10, 20 ,t]代表栈中a[1]~a[9]的元素大于a[10]的元素,索引为a[11]~a[19]的元素大于a[20]的元素...
这样我们可以发现在a[10]左边第一个比a[10]的数为a[0],在a[20]左边第一个比a[20]的数为a[10]...
如何实现呢?
每次有元素a[i] = x进栈时,若栈顶的元素a[t] >= x,出栈,否则进栈,当然栈为空时直接进栈
栈顶元素出栈时,由于栈顶比要入栈的元素大,所以a[t]右边第一个比a[t]小的值为x

这样我们就能够求一个数x左边和右边第一个比x小的数

例题84. Largest Rectangle in Histogram

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        stack<int> s;
        int area = 0;
        heights.push_back(0);
        for(int i = 0; i < heights.size(); i++){
            while(!s.empty() && heights[s.top()] >= heights[i]) {
                int j = s.top();
                s.pop();
                area = max(area, heights[j] * (s.size() == 0 ? i: (i - (s.top() + 1)) ));
            }

            s.push(i);
        }

        return area;
    }
};

85. Maximal Rectangle
与上题类似,需要处理一下

单调队列

特点

队列中元素单调,队首,队尾可以出队,队尾可以入队
区间最值问题可以使用ST表和线段树来解决,但经常用于多次查询,时间复杂度为\(O(n\lg(n))\)
如果是类似于滑动窗口的区间最值,则可以用单调队列来解决,时间复杂度为\(O(n)\)
例题239. Sliding Window Maximum
这是个单调递减队列,队列维护滑动窗口,队列中位于前面且比后面小的一定不会是最大值,所以入队时可以踢出
理解了单调栈,单调队列也比较好理解
单调队列其他题目

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> q;
        vector<int> v;
        for(int i = 0; i < k - 1; i++){
            while( !q.empty() && nums[q.back()] <= nums[i]) q.pop_back();

            q.push_back(i);
        }

        for(int i = k-1; i < nums.size(); i++){
            while( !q.empty() && nums[q.back()] <= nums[i]) q.pop_back();
            q.push_back(i);
            while( !q.empty() &&  i - q.front() >= k) q.pop_front();
            v.push_back(nums[q.front()]);
        }

        return v;
    }
};

862. Shortest Subarray with Sum at Least K

class Solution {
public:
    int shortestSubarray(vector<int>& A, int K) {
        int sum = 0;
        deque<int> q;
        int l = A.size() + 1;

        vector<int> B(l, 0);
        B[0] = 0;
        for(int i = 0; i < A.size(); i++) B[i+1] = B[i] + A[i];
        for(int i = 0; i < B.size(); i++){

            while(!q.empty() && B[i] <= B[q.back()]) q.pop_back();
            q.push_back(i);
            while(!q.empty() && B[i] - B[q.front()] >= K) {
                l = min(l, i - q.front());
                q.pop_front();
            }

        }

        return l == A.size() + 1 ? -1 : l;
    }
};  

使用单调队列的基本步骤大致为

1.满足单调性

当遍历到a[i]时,需要将a[i]入栈,首先从队列弹出在未来的遍历中一定比a[i]劣的值
在滑动窗口中的
while( !q.empty() && nums[q.back()] <= nums[i]) q.pop_back();
表示在未来的滑动窗口中一定不会使用比之前小于等于nums[i]的值

与862题中的
while(!q.empty() && B[i] <= B[q.back()]) q.pop_back();
在未来的前缀和中如果需要B[i]为首,那么B[i]越小越好,在B[i]之前且小于等于B[i]的前缀和比B[i]劣

2.i入队列

3.满足具体约束

在滑动窗口中
具体的约束为区间长度
while( !q.empty() &&  i - q.front() >= k) q.pop_front();

在862题中表示为
while(!q.empty() && B[i] - B[q.front()] >= K) {
            l = min(l, i - q.front());
            q.pop_front();
        }

原文地址:https://www.cnblogs.com/qbits/p/10934055.html

时间: 2024-10-09 11:57:03

单调栈与单调队列的相关文章

单调栈以及单调队列

单调栈: 定义: 栈内的元素,按照某种方式排列下(单调递增或者单调递减),如果新入栈的元素破坏了单调性,就弹出栈内元素,直至满足单调性. 作用:单调栈可以找到从左/右遍历第一个比它大/小的元素的位置.时间复杂度为O(N): 实现方式:(以维护单调递增栈为例) 进栈操作:每次进入栈时,先检验栈顶元素和进栈元素的大小,如果小于,那么直接入栈:否则,大于等于进栈元素的出栈,直到栈空或者栈顶元素小于入栈元素. 例如:3 8 2 3 1 初始时刻栈为空,3入栈.......................

单调栈和单调队列

单调栈: 用于解决求出距离当前值最近的满足某个性质的值 1 给定一个长度为N的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出-1. 2 3 输入格式 4 第一行包含整数N,表示数列长度. 5 6 第二行包含N个整数,表示整数数列. 7 8 输出格式 9 共一行,包含N个整数,其中第i个数表示第i个数的左边第一个比它小的数,如果不存在则输出-1. 10 11 数据范围 12 1≤N≤105 13 1≤数列中元素≤109 14 输入样例: 15 5 16 3 4 2 7 5 17 输出样

POJ 2559 Largest Rectangle in a Histogram(单调栈) &amp;&amp; 单调栈

嗯... 题目链接:http://poj.org/problem?id=2559 一.单调栈: 1.性质: 单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性,可能为单调递增,也可能为单调递减. 2.模样: 这是一个单调递增的栈,如果我们插入的元素大于栈顶元素,则直接入栈: 如果我们插入的元素小于栈顶,则需要把栈内所有大于它的元素暂时出栈,将这个元素入栈后,再将暂时出栈的元素入栈,维护单调性. 二.模板: 这道题是单调栈的一道模板题: 先思考一个问题,如果题目中的矩形的高度都是单调递增

小结:单调栈 &amp; 单调队列

概要: 对于维护信息具有单调性的性质或者问题可以转化为具有单调性质的模型的题,我们可以考虑用单调栈或单调队列. 技巧及注意: 技巧很多,只要能将问题转化为单调性问题,就好解决了. 当维护固定长度的单调区间,我们考虑用单调队列,如[BZOJ]3314: [Usaco2013 Nov]Crowded Cows(单调队列) 单调栈维护长度时要进行及时更新,例如:[BZOJ]3039: 玉蟾宫(DP/单调栈) 假设完美状态后再进行减法原理,例如:[BZOJ]1628 && 1683: [Usaco

单调栈&amp;单调队列入门

单调队列是什么呢?可以直接从问题开始来展开. Poj 2823 给定一个数列,从左至右输出每个长度为m的数列段内的最小数和最大数. 数列长度:\(N <=10^6 ,m<=N\) 解法① 很直观的一种解法,那就是从数列的开头,将窗放上去,然后找到这最开始的k个数的最大值,然后窗最后移一个单元,继续找到k个数中的最大值. 这种方法每求一个f(i),都要进行k-1次的比较,复杂度为$ O(Nk) $. 显然,如果暴力时间复杂度为 $ O(Nm) $ 不超时就怪了. 解法② 还有一种想法是维护一个B

单调队列&amp;单调栈归纳

单调队列 求长度为M的区间内的最大(小)值 单调队列的基本操作,也就是经典的滑动窗口问题. 求长度为M的区间内最大值和最小值的最大差值 两个单调队列,求出长度为M的区间最大最小值的数组,分别求最大最小值. 求边长为a的正方形内最大值和最小值的最大差值([HAOI2007]理想的正方形) 一个大体的思路是先分别求出以i,j为左上端点的边长为a的矩形中的最大值和最小值.那么该怎么做?,先用单调队列求出一个点右边a个单位的最大值,形成一个新矩阵,再求出新矩阵下边a个单位的最大值.然后最小值再求一边,最

单调队列 单调栈

建议不了解STL的读者先了解几个基本的队列的STL.这也是单调队列和单调栈一般都会用到的. 单调队列:建立一个队列,使队列一直具有单调性(满足单调递增或者单调递减),时间复杂度O(N). 那么我们应该如何做到"使队列一直具有单调性"呢? 以单调递增为例,我们O(N)扫描整个序列,每扫描到一个元素: 1 如果该元素大于等于队列末尾元素,则直接入队; 2 而如果该元素小于已有队列的末尾元素,即不满足单调递增,则使队列中的末尾元素出队,直到该元素符合入队条件,然后入队. 如果只到这里,那么我

7.14 单调栈 单调队列 +dp优化

单调栈和单调队列的定义具体看ppt了 模板: 单调队列 head =1; tail = 0; rep( i ,1 ,n ){ while( head <= tail && a[i] < dq[tail].first)tail--; while( head <= tail && dq[head].second < i-k+1) head++; dq[ ++tail ]={a[i] ,i}; 例题:https://vjudge.net/contest/3

单调栈&amp;单调队列

最近打了三场比赛疯狂碰到单调栈和单调队列的题目,第一,二两场每场各一个单调栈,第三场就碰到单调队列了.于是乎就查各种博客,找单调栈,单调队列的模板题去做,搞着搞着发现其实这两个其实是一回事,只不过利用了容器内元素单调的不同特性,用来加速处理不同的问题. 单调栈解决的是以某个值为最小(最大)值的最大区间,维护的是左右两边第一个比当前位大或者小的数 单调队列解决的是区间最小(最大)值,维护的是区间内的最值 举个栗子,a[i] = { 1,6,3,5,1,2,4 } 如果容器内数字是单调递减的,最后得