单调队列的介绍
由于现在我也没接触过正经的单调队列的定义,因而引申为介绍,
单调队列,类似与单调栈,存储在单调队列里面的元素理应都是单调的,单调队列的基础使用deque(双端队列)去实现的,因而我们的队头和队尾都可以去实现插入和删除,因而更方便去实现单调队列的功能
适用问题场景
适用于去求一个区间里面的极值(极大值或者极小值)
单调队列的应用
P1886 滑动窗口 /【模板】单调队列
题意:给你一个长度为n的序列,告诉你窗口的长度为k,然后问你滑动窗口中每次出现的最小值和最大值是什么
思路:很经典的一道单调队列题目,我们可以 用单调队列去实现滑动窗口,输出每次的到的结果,最小值或者最大值,既然要先求最小值,那么我们就用一个单调递减队列,先确定队头元素是否小于窗口覆盖范围,要是小于窗口覆盖范围,就先将队头元素弹出,然后比较加入元素比队尾元素小的,就将队尾元素弹出
然后再输出最大值,将队列中的元素全部清空,然后变成单调递增栈,还是先确定队头元素是否滑出窗口,如果滑出窗口范围就直接弹出,然后比较要加进来的元素和队尾元素的大小,如果大于队尾元素,那么就将队尾元素弹出
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int a[1000005];
deque<int> que;//存储下标的双端队列signed main()
{cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++)//先输出最小值 {if(!que.empty()&&i-k+1>que.front()){que.pop_front();}while(!que.empty()&&a[i]<a[que.back()]){que.pop_back();}que.push_back(i);if(i>=k){cout<<a[que.front()]<<" ";}}cout<<"\n";que.clear();for(int i=1;i<=n;i++){if(!que.empty()&&i-k+1>que.front()){que.pop_front();}while(!que.empty()&&a[i]>a[que.back()]){que.pop_back();}que.push_back(i);if(i>=k){cout<<a[que.front()]<<" ";}}cout<<"\n";return 0;
}
1438. 绝对差不超过限制的最长连续子数组
题意:就是说样例会给你一个数组,和一个限制数limit,一个区间里面的最大值和最小值的差应当小于这个限制数
用两个单调队列,一个单调队列用于存储最小值,一个单调队列用来存储最大值,最后去判断极差是否在这个限制数之内,如果处于这个限制数之内,则可以进行最大长度的更新
思路:用一个单调递增队列去记录这个区间内的最大值,用一个单调递减队列去记录区间内的最小值,然后利用滑动窗口的思想,设置一个左端的L和右端R,去遍历窗口的情况,窗口一开始应该先移动右指针,不断去扩大窗口内的元素数量知道窗口内的最大值和最小值的差值超出了限制数,一旦超出限制数就要去增加左指针缩小窗口范围
class Solution {
public:int longestSubarray(vector<int>& nums, int limit) {deque<int>qmin,qmax;int ans=0;//用于更新最大长度int l=0,r=0;while(r<nums.size()){while(!qmin.empty()&&nums[r]<nums[qmin.back()])qmin.pop_back();while(!qmax.empty()&&nums[r]>nums[qmax.back()])qmax.pop_back();qmin.push_back(r);qmax.push_back(r);while(!qmax.empty()&&!qmin.empty()&&nums[qmax.front()]-nums[qmin.front()]>limit){if(nums[l]==nums[qmax.front()])qmax.pop_front();if(nums[l]==nums[qmin.front()])qmin.pop_front();l++;}ans=max(ans,r-l+1);r++;}return ans;}
};
单调队列初总结
其实单调队列就是用于求一个区间的极值的一种方法,我们可以利用单调队列去处理某一个区间里面的极值问题,然后就会衍生出一种滑动窗口的思想