单调队列实现滑动窗口
单调队列实现滑动窗口
单调队列和单调栈都有一个共同的思路,删去一些无用的元素,使得整个数据结构有序。
关键:单调队列的实现是使用双端队列来实现的,使用pop_back让队尾不符合序列序的弹出,并且查询队头的元素。
其中单调队列是使用双端队列实现的
经典应用:求滑动窗口中最大(小)值。
在这个例子中,因为-3进入了窗口并且比3小,因此,发现如果-3存在于这个队列中,那么3就没有存在的必要,因此pop_back出3,同理-1也被pop_back。
处理细节:
- 单调队列中存储的是原数组a中的下标
- 单调队列是从队尾到队头是上升的还是下降的?即hh维护的是哪一个最值(上升是维护最大值)
- 遇到破坏单调性的a[i]时,应该把单调队列后面的元素pop_back()出来,再加入这个元素。
上面所描述的第三点具体到图片为:
// Author: oceanlvr
#include <bits/stdc++.h>
using namespace std;
static int faster_iostream = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
return 0;
}();
const int maxn = 1e6 + 10;
int q[maxn], a[maxn];
int hh, tt = -1;
int n, k;
int main() {
cin >> n >> k;
/*性质查询:查询头元素(因为是单调的队列)
滑动窗口最小值,即查找队尾到队头是从大到小的队列队头
滑动窗口最大值,即查找队尾到队头是从小到大的队列队头
*/
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) {
//队尾到队头是从大到小的单调队列
if (tt >= hh && i - k + 1 > q[hh]) hh++;
while (tt >= hh && a[q[tt]] >= a[i]) tt--;
q[++tt] = i;
if (i - k + 1 >= 0) cout << a[q[hh]] << " ";
}
cout << endl;
hh = 0, tt = -1;
for (int i = 0; i < n; i++) {
//队尾到队头是从小到大的单调队列
if (tt >= hh && i - k + 1 > q[hh]) hh++;
while (tt >= hh && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
if (i - k + 1 >= 0) cout << a[q[hh]] << " ";
}
cout << endl;
return 0;
}
// https://www.luogu.com.cn/problem/P2251
//#define judge
// Author: oceanlvr
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, k;
int hh, tt = -1;
const int maxn = 1e6 + 10;
int a[maxn], q[maxn];
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> a[i];
hh = 0, tt = -1;
for (int i = 0; i < n; i++) {
if (tt >= hh && i - k + 1 > q[hh]) hh++;
while (tt >= hh && a[i] <= a[q[tt]]) tt--;
q[++tt] = i;
if (i - k + 1 >= 0) cout << a[q[hh]] << endl;
}
return 0;
}