单调栈
给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int main()
{
int n, k;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
stack<int> s;
for (int i = 0; i < n; i++)
{
while (!s.empty() && s.top() >= a[i]) s.pop();
if (s.empty()) cout << -1 << " ";
else cout << s.top() << " ";
s.push(a[i]);
}
return 0;
}
单调队列
一维滑动窗口
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。你只能在窗口中看到 k 个数字。每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int main()
{
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> a[i];
deque<int> q;
for (int i = 0; i < n; i++)
{
if (!q.empty() && q.back() - q.front() + 1 >= k) q.pop_front();//注意维护队列的长度
while (!q.empty() && a[q.back()] >= a[i]) q.pop_back();
q.push_back(i);//队列中存的是数组下标,为了便于维护队列长度
if (i >= k - 1) cout << a[q.front()] << " ";
}
q.clear();
puts("");
for (int i = 0; i < n; i++)
{
if (!q.empty() && q.back() - q.front() + 1 >= k) q.pop_front();
while (!q.empty() && a[q.back()] <= a[i]) q.pop_back();
q.push_back(i);
if (i >= k - 1) cout << a[q.front()] << " ";
}
return 0;
}
二维滑动窗口
可以求
n
×
m
n \times m
n×m 的矩阵中所有的大小一定的子矩阵的最值(假设子矩阵大小为
a
×
b
a \times b
a×b)。
在
O
(
n
m
)
O(nm)
O(nm) 的时间内求出所有的子矩阵的最值。
Code
- a为n * m 的矩阵,r 为每行中滑动窗口的最值,f为 a * b的子矩阵中的最值
- 由于队列中存的是下标,且在维护队列的单调性的时候会用到操作数组(会用到同一行或同一列中前面的点)
所以不能直接将结果存在操作数组中,需要另外的空间开销,创建新数组。
//求出所有行中的长度为b的窗口中的最小值
for (int i = 1; i <= n; i++)
{
//操作数组为a,结果记录为r
deque <int> q;
for (int j = 1; j <= m; j++)
{
if (!q.empty() && q.back() - q.front() + 1 >= b) q.pop_front();
while (!q.empty() && a[i][q.back()] >= a[i][j]) q.pop_back();
q.push_back(j);
r[i][j] = a[i][q.front()];
}
}
//求出所有列中的长度为a的窗口中的最小值(实质为a * b的矩阵的最小值)
for (int j = 1; j <= m; j++)
{
//操作数组为r,结果记录为f
deque<int> q;
for (int i = 1; i <= n; i++)
{
if (!q.empty() && q.back() - q.front() + 1 >= a) q.pop_front();
while (!q.empty() && r[q.back()][j] >= r[i][j]) q.pop_back();//
q.push_back(i);
f[i][j] = r[q.front()][j];
}
}