单调队列#
常见模型:找出滑动窗口中的最大值/最小值
是一种主要用于解决滑动窗口类问题的数据结构,即在长度为n 的序列中,求每个长度为 k的区间的区间最值。
用于解决滑动窗口和双指针解决不了的问题
https://www.bilibili.com/video/BV1H5411j7o6
求最大值三种情况:
队尾入队:
如果新进入窗口的值大于队尾元素,则队尾出队列,再将将新进入的元素入队
否则直接入队
队头出队:
若队头是否滑出了窗口,队头出队
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int a[N];
int main()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) cin >> a[i];//读入数据
deque<int> q;
for(int i = 1; i <= n; i++)
{
while(q.size() && q.back() > a[i]) //新进入窗口的值小于队尾元素,则队尾出队列
q.pop_back();
q.push_back(a[i]);//将新进入的元素入队
if(i - k >= 1 && q.front() == a[i - k])//若队头是否滑出了窗口,队头出队
q.pop_front();
if(i >= k)//当窗口形成,输出队头对应的值
cout << q.front() <<" ";
}
q.clear();
cout << endl;
//最小值亦然
for(int i = 1; i <= n; i++)
{
while(q.size()&&q.back()<a[i]) q.pop_back();
q.push_back(a[i]);
if(i-k>=1&&a[i-k]==q.front()) q.pop_front();
if(i>=k) cout<<q.front()<<" ";
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class Solution {
public:
vector<int> maxInWindows(vector<int>& nums, int k) {
vector<int> ans;
deque<int> q;
for(int i =0;i<nums.size();i++){
while(q.size()&&q.back()<nums[i]) q.pop_back();
q.push_back(nums[i]);
if(i-k>=0&&nums[i-k]==q.front()) q.pop_front();
if(i>=k-1) ans.push_back(q.front());
}
// int q[100010];
// int h = 0,t = 0;
// for(int i = 0;i<nums.size();i++){
// if(h<=t&&q[h]<i-k+1) h++;
// while(h<=t&&nums[i]>=nums[q[t]]) t--;
// q[++t] = i;
// if(i>=k-1) ans.push_back(nums[q[h]]);
// }
return ans;
}
};
|
单调队列习题集
单调栈#
常见模型:找出每个数左边离它最近的比它大/小的数
1、题目(来源于AcWing):#
给定一个长度为N的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出-1。
输入格式
第一行包含整数N,表示数列长度。
第二行包含N个整数,表示整数数列。
输出格式
共一行,包含N个整数,其中第i个数表示第i个数的左边第一个比它小的数,如果不存在则输出-1。
数据范围
1≤N≤105
1≤数列中元素≤109
输入样例:
5
3 4 2 7 5
输出样例:
-1 3 -1 2 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
stack<int> stk;
int main()
{
int m;
cin >> m;
while (m -- )
{
int x;
cin >> x;
while (stk.size() && stk.top() >= x) stk.pop();//在x入栈前删掉比x大的数
if (stk.size() == 0) cout << "-1 ";
else cout << stk.top() << ' ';
stk.push(x);//x入栈
}
return 0;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int h[N], q[N], l[N], r[N];
//l[i]表示i左边第一个比h[i]小的元素的下标,r[i]表示i右边第一个比h[i]小的元素的下标
typedef long long LL;
int main()
{
int n;
while(cin>>n, n)
{
for(int i = 1; i <= n; i ++) scanf("%d", &h[i]);
stack<int> st;
for(int i = 1; i <= n; i ++)
{
while(st.size()&&h[st.top()]>=h[i]) st.pop();
if(st.size() == 0) l[i]=0;
else l[i]=st.top();
st.push(i);
}
stack<int> st2;
for(int i = n; i>=1; i --)
{
while(st2.size()&&h[st2.top()]>=h[i]) st2.pop();
if(st2.size() == 0) r[i]=n+1;
else r[i]=st2.top();
st2.push(i);
}
LL res = 0;
for(int i = 1; i <= n; i ++)
res = max(res, (LL)h[i] * (r[i] - l[i] - 1));
printf("%lld\n", res);
}
return 0;
}
|
单调栈习题集