单调队列实现滑动窗口

单调队列实现滑动窗口

单调队列和单调栈都有一个共同的思路,删去一些无用的元素,使得整个数据结构有序。

关键:单调队列的实现是使用双端队列来实现的,使用pop_back让队尾不符合序列序的弹出,并且查询队头的元素。

其中单调队列是使用双端队列实现的

经典应用:求滑动窗口中最大(小)值。

在这个例子中,因为-3进入了窗口并且比3小,因此,发现如果-3存在于这个队列中,那么3就没有存在的必要,因此pop_back出3,同理-1也被pop_back。

滑动窗口

处理细节:

  1. 单调队列中存储的是原数组a中的下标
  2. 单调队列是从队尾到队头是上升的还是下降的?即hh维护的是哪一个最值(上升是维护最大值)
  3. 遇到破坏单调性的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;
}

P2251洛谷 滑动窗口求最小值

// 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;
}
posted @ 2020-03-11 00:08  AdaMeta730  阅读(802)  评论(0)    收藏  举报