给定一个大小为 n106 的数组。

有一个大小为 k的滑动窗口,它从数组的最左边移动到最右边。

你只能在窗口中看到 k 个数字。

每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为 [1 3 -1 -3 5 3 6 7]k3

窗口位置 最小值 最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 7

你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式

输入包含两行。

第一行包含两个整数 nk,分别代表数组长度和滑动窗口的长度。

第二行有 n个整数,代表数组的具体数值。

同行数据之间用空格隔开。

输出格式

输出包含两个。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

输入样例:

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

输出样例:

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

参考代码:

#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 ++ )
    {
        if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;

        while (hh <= tt && a[q[tt]] >= a[i]) tt -- ;
        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 ++ ;

        while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
        q[ ++ tt] = i;

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

    puts("");

    return 0;
}

1 条评论

  • @ 2024-1-31 21:11:12

    定义

    顾名思义,单调队列的重点分为「单调」和「队列」。

    「单调」指的是元素的「规律」——递增(或递减)。

    「队列」指的是元素只能从队头和队尾进行操作。

    Ps. 单调队列中的 "队列" 与正常的队列有一定的区别,稍后会提到

    例题分析

    解释

    有了上面「单调队列」的概念,很容易想到用单调队列进行优化。

    要求的是每连续的 个数中的最大(最小)值,很明显,当一个数进入所要 "寻找" 最大值的范围中时,若这个数比其前面(先进队)的数要大,显然,前面的数会比这个数先出队且不再可能是最大值。

    也就是说——当满足以上条件时,可将前面的数 "弹出",再将该数真正 push 进队尾。

    这就相当于维护了一个递减的队列,符合单调队列的定义,减少了重复的比较次数,不仅如此,由于维护出的队伍是查询范围内的且是递减的,队头必定是该查询区域内的最大值,因此输出时只需输出队头即可。

    显而易见的是,在这样的算法中,每个数只要进队与出队各一次,因此时间复杂度被降到了

    而由于查询区间长度是固定的,超出查询空间的值再大也不能输出,因此还需要 site 数组记录第 个队中的数在原数组中的位置,以弹出越界的队头。

    过程

    例如我们构造一个单调递增的队列会如下:

    原序列为:

    |

    1
    

    |

    1 3 -1 -3 5 3 6 7
    

    | | ------------ | ----------------------------- |

    因为我们始终要维护队列保证其 递增 的特点,所以会有如下的事情发生:

    操作 队列状态
    1 入队 {1}
    3 比 1 大,3 入队 {1 3}
    -1 比队列中所有元素小,所以清空队列 -1 入队 {-1}
    -3 比队列中所有元素小,所以清空队列 -3 入队 {-3}
    5 比 -3 大,直接入队 {-3 5}
    3 比 5 小,5 出队,3 入队 {-3 3}
    -3 已经在窗体外,所以 -3 出队;6 比 3 大,6 入队 {3 6}
    7 比 6 大,7 入队 {3 6 7}
    • 1