何为单调队列?,单调队列是指一个队列内部的元素具有严格单调性的数据结构,分为单调递增队列和单调递减队列,单调队列需要满足两个性质:
1>单调队列必须满足从队首至队尾的严格单调性。
2>排在队列前面的元素要比排在队列后面的元素先进队。
单调队列问题也可以被称为"滑动窗口"为问题,它是维持一个单调窗口,元素进队列时,需要与队尾元素进行比较,在维持的窗口单调递增的情况下,如果该元素大于队尾元素,可以直接将该元素扔进队列,否则不断将队尾元素出队(–tail),直至满足该元素大于队尾元素。
题目描述
有一个长度为
n
(
n
<
=
1
e
6
)
n(n <= 1e6)
n(n<=1e6),假设有一个长度为
k
k
k的滑动窗口从数组的最左边一直滑倒数组的最右边。在窗口进行滑动时,我们只能看到窗口内的
k
k
k个数组,且窗口每次只能向右滑动一个元素,假设数组为[1 3 -1 -3 5 3 6 7],判断窗口每个位置的最小值和最大值。
[Sample input]
8 3
1 3 -1 -3 5 3 6 7
[Sample output]
-1 -3 -3 -3 3 3
3 3 5 5 6 7
倘若以最大值为例,此时该队列为一个单调递减队列,元素从左至右依次入队,入队必然从队列尾部开始入队,此时与队列尾部元素进行比较,如果比队列元素小就直接扔进队列,否则删除当前队尾元素继续比较,直到找到一个比队尾元素小或为空的情况,在扔进队列。如果队列的大小超过窗口值则删除队首元素,直至队列大小小于窗口值为止,然后将当前元素插入队尾,每次取队首元素(最大值)即可
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 6;//1000006
int arr[N], n, k;
int pos[N], que[N], MAX[N], MIN[N];
//8 3(窗口大小为 3)
//1 3 -1 -3 5 3 6 7
void get_Min()
{
int head = 1, tail = 0;
// 满足队列 的单调性为单调递增
for(int i = 1; i <= n; ++i)
{
while(head <= tail && que[tail] > arr[i]) --tail;//队尾元素 > 当前元素, tail前移至 队尾元素 <= 当前元素//插入
que[++tail] = arr[i];
pos[tail] = i;
if(i < k) continue;//窗口大小未至 k
while(pos[head] < i - k + 1) ++head;//删队头, 因为在滑动过程中, 队头元素会被舍弃
MIN[i - k + 1] = que[head];
}
for(int i = 1; i <= n - k + 1; ++i) printf("%d ", MIN[i]);
printf("\n");
}
void get_Max()
{
int head = 1, tail = 0;//队首 --> 1, 队尾 --> 0
// 满足队列 的单调性为单调递减
for(int i = 1; i <= n; ++i)
{
while(head <= tail && que[tail] <= arr[i]) --tail;//队尾元素 <= 当前元素, tail 前移直至 队尾元素 > 当前元素//插入
que[++tail] = arr[i];
pos[tail] = i;
if(i < k) continue;//窗口大小未至 k
while(pos[head] < i - k + 1) head++; //滑动窗口过大即超过了 k, 删队头
MAX[i - k + 1] = que[head]; //取队头(保证在当前滑动窗口内最大)元素
}
for(int i = 1; i <= n - k + 1; ++i) printf("%d ", MAX[i]);
printf("\n");
}
int main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i) scanf("%d", &arr[i]);
get_Min();
get_Max();
return 0;
}