单调队列
单调队列是指:
- 队列中元素之间的关系具有单调性
- 队首和队尾都可以进行出队操作
- 只有队尾可以进行入队操作
解题基本思想:
用途:解决区间最值问题
维护队首元素作为答案,去掉多余的元素(维护单调性)。时间复杂度 O(N)
实现方式:
- deque 双向队列 (c++容器) 头文件#include < queue > 提供
deque<int > q;
q.push_back(); // 入队
q.pop_back(); // 从队尾出队
q.pop_front(); // 从队首出队
q.front(); // 获得队首元素
- 数组模拟 实现双向队列
int que[100];
int head = 0, tail = 0;
//进队
void push(int a)
{
que[++tail] = a;
}
//出队
int pop()
{
return que[++head];
}
//判断队列是否为空
bool empty()
{
return !(head < tail);
}
滑动窗口问题 (洛谷 P1886)
输出给定窗口的 最小值 和最大值 序列
思路:
考虑三个地方
- 维护入队 ,当元素从队尾 入队, 对前面的 元素的 比较 (维护单调性)与删除(不符合单调性)
当求最大值序列时,一个元素入队, 需保证 队列中 在它之前的,比它小的 元素都被删除(维护队列 单调递减),
原因: 当前值 为 较大值 时,显然 之前的较小值 在同一窗口 已无用
当求最小值序列时,一个元素入队, 需保证 队列中 在它之前的,比它大的 元素都被删除(维护队列 单调递增)
原因: 当前值 为 较小值 时,显然 之前的较大值 在同一窗口 已无用
- 维护队首 容器队首的元素已经不属于当前窗口的边界,则应该将队首删除
- 答案出队,当前位置 满足窗口大小 则记录答案
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
vector<int> mx(int a[],int n,int k) // 求 滑动窗口 最大值序列
{
vector<int> ans;
deque<int >q1;
for(int i=1; i<=n; i++)
{
while(!q1.empty()&&a[q1.back()]<a[i]) // 与队尾元素比较,保证 队列 递减,(即删除前面比a[i]小的数)
q1.pop_back();
while(!q1.empty()&&i-q1.front()+1>k) // 与队首 位置比较,判断是否 在当前窗口
q1.pop_front();
q1.push_back(i); // 元素 入队 (下标入队)
if(i>=k) ans.push_back(a[q1.front()]); // 记录答案
}
return ans;
}
vector<int> mi(int a[],int n,int k) // 求 滑动窗口 最小值序列
{
vector<int> ans;
deque<int >q1;
for(int i=1; i<=n; i++)
{
while(!q1.empty()&&a[q1.back()]>a[i])
q1.pop_back();
while(!q1.empty()&&i-q1.front()+1>k)
q1.pop_front();
q1.push_back(i);
if(i>=k)
ans.push_back(a[q1.front()]);
}
return ans;
}
int a[N];
int main()
{
ios::sync_with_stdio(false);
int n,k;
cin>>n>>k;
for(int i=1; i<=n; i++)
{
cin>>a[i];
}
vector<int >c1,c2;
c1=mi(a,n,k);
c2=mx(a,n,k);
//输出答案
for(int i=0;i<c1.size();i++)
{
i!=c1.size()-1 ? cout<<c1[i]<<" ":cout<<c1[i]<<endl;
}
for(int i=0;i<c2.size();i++)
{
i!=c2.size()-1 ? cout<<c2[i]<<" ":cout<<c2[i]<<endl;
}
return 0;
}