题目描述

原题

题目大意&链接

给定一个长度为 n 的数组,和一个大小为 k 的滑动窗口,滑动窗口从数组的最左端移动到最右端,每次移动一个位置,要求输出每个窗口中的最小值和最大值。

输入输出样例:
输入:

8 3
1 3 -1 -3 5 3 6 7

输出:

-1 -3 -3 -3 3 3
3 3 5 5 6 7

详情:ac_154滑动窗口

解题思路

单调队列:队列中的元素保持单调递增或单调递减:

  1. 对于最小值问题,维护一个单调递增队列:队列头部是当前窗口的最小值,新元素入队时,删除队列中所有比它大的元素,以保持队列单调递增。
    int hh = 0, tt = -1;
    for (int i = 0; i < n; i++)
    {
    //判断队头元素是否还在队列里面,不应该在,则弹出队头元素(保持 k 个元素)
    if (hh <= tt && i - k + 1 > q[hh]) hh++;
    //队列不为空且队尾元素大于等于 x 时,弹出队尾元素(使队列具有单调性)
    while (hh <= tt && a[q[tt]] >= a[i]) tt--;
    //当前元素下标 i 入队
    q[++tt] = i;

    if (i >= k - 1) printf("%d ", a[q[hh]]);
    }
  2. 对于最大值问题,维护一个单调递减队列:队列头部是当前窗口的最大值,新元素入队时,删除队列中所有比它小的元素,以保持队列单调递减。
    hh = 0, tt = -1;
    for (int i = 0; i < n; i++)
    {
    if (hh <= tt && i - k + 1 > q[hh]) hh++;
    //队列不为空且队尾元素小于等于 x 时,弹出队尾元素(使队列具有单调性)
    while (hh <= tt && a[q[tt]] <= a[i]) tt--;
    //当前元素下标 i 入队
    q[++tt] = i;

    if (i >= k - 1) printf("%d ", a[q[hh]]);
    }

完整代码

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

const int N = 1000010;

int a[N], q[N]; //数组 单调队列:存储元素下标

int main()
{
int n, k;
scanf("%d %d", &n, &k);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);

int hh = 0, tt = -1;
for (int i = 0; i < n; i++)
{
//判断队头元素是否还在队列里面,不应该在,则弹出队头元素(保持 k 个元素)
if (hh <= tt && i - k + 1 > q[hh]) hh++;
//队列不为空且队尾元素大于等于 x 时,弹出队尾元素(使队列具有单调性)
while (hh <= tt && a[q[tt]] >= a[i]) tt--;
//当前元素下标 i 入队
q[++tt] = i;

if (i >= k - 1) printf("%d ", a[q[hh]]);
}

puts("");

hh = 0, tt = -1;
for (int i = 0; i < n; i++)
{
if (hh <= tt && i - k + 1 > q[hh]) hh++;
//队列不为空且队尾元素小于等于 x 时,弹出队尾元素(使队列具有单调性)
while (hh <= tt && a[q[tt]] <= a[i]) tt--;
//当前元素下标 i 入队
q[++tt] = i;

if (i >= k - 1) printf("%d ", a[q[hh]]);
}

puts("");

return 0;
}

时间和空间复杂度

时间复杂度:O(n)
空间复杂度:O(n)