单调队列 Monotonic Queue

单调队列 Monotonic Queue

例题POJ 2823 Sliding Window,长度为nn的数组,编程输出每kk个连续的数中的最大和最小值。

笨办法每次都取kk个数来比较,所以复杂度是O(nk)O(nk),单调队列方法尽量复用之间做的比较结果,可以做到一遍扫描完成,复杂度O(n)

单调队列算法不变量如下:以找最小值为例,我们维护当前kk个字符窗口中的一个单调增的数字的序列(队列),保证队列头部(最小)的数字是窗口中的最小值。

依次向右扫描新的数字,做两个操作就可以保证这个不变量:

这样每个数字最多进出队列一次,队列用nn长度的下标数组维护,算法复杂度O(n)O(n)

参考代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define maxn 1000100
using namespace std;
int q[maxn], a[maxn];
int n, k;
 
void getmin() {  // 得到这个队列里的最小值,直接找到最后的就行了
  int head = 0, tail = 0;
  for (int i = 1; i < k; i++) {
    while (head <= tail && a[q[tail]] >= a[i]) tail--;
    q[++tail] = i;
  }
  for (int i = k; i <= n; i++) {
    while (head <= tail && a[q[tail]] >= a[i]) tail--;
    q[++tail] = i;
    while (q[head] <= i - k) head++;
    printf("%d ", a[q[head]]);
  }
}
 
void getmax() {  // 和上面同理
  int head = 0, tail = 0;
  for (int i = 1; i < k; i++) {
    while (head <= tail && a[q[tail]] <= a[i]) tail--;
    q[++tail] = i;
  }
  for (int i = k; i <= n; i++) {
    while (head <= tail && a[q[tail]] <= a[i]) tail--;
    q[++tail] = i;
    while (q[head] <= i - k) head++;
    printf("%d ", a[q[head]]);
  }
}
 
int main() {
  scanf("%d%d", &n, &k);
  for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
  getmin();
  printf("\n");
  getmax();
  printf("\n");
  return 0;
}

习题