【题目来源】
https://www.luogu.com.cn/problem/P1886
https://www.acwing.com/problem/content/156/

【题目描述】
给定一个大小为 n≤10^6 的数组。
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k 个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7],k 为  3 。

窗口位置最小值最大值
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37

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

【输入格式】
输入包含两行。
第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。
第二行有 n 个整数,代表数组的具体数值。
同行数据之间用空格隔开。

【输出格式】
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。

【输入样例】
8 3
1 3 -1 -3 5 3 6 7

【输出样例】
-1 -3 -3 -3 3 3
3 3 5 5 6 7

【算法分析】
● 若设队列头指针为 h,尾指针为 t,则本题给出了 h=0,t=-1 及 h=1,t=0 时的两种代码。
不过,这两种取法均满足
t-h+1=0 的约束条件。

● 单调队列应用于“滑动窗口”问题时确立相关元素下标的示意图(https://blog.csdn.net/hnjzsyjyj/article/details/144599941)
(1)若“滑动窗口”大小为 k,给定的数组各元素下标从 0 开始,则据上图可得“滑动窗口”左端元素下标 ≤i-k 时才能满足“滑动窗口”中的元素个数 >k,此时才能依据题意出队“滑动窗口”中的队首元素。另外,由于数组元素下标从 0 开始,所以当 i≥k-1 时,“滑动窗口”中的元素个数才能满足 ≥k 的约束条件,此时才能依据题意输出“滑动窗口”中队首元素的值。

(2)若“滑动窗口”大小为 k,给定的数组各元素下标从 1 开始,则据上图可得“滑动窗口”左端元素下标 <i-k+1 时才能满足“滑动窗口”中的元素个数 >k,此时才能依据题意出队“滑动窗口”中的队首元素。另外,由于数组元素下标从 1 开始,所以当 i≥k 时,“滑动窗口”中的元素个数才能满足 ≥k 的约束条件,此时才能依据题意输出“滑动窗口”中队首元素的值。

(3)实践证明,不管给定的数组各元素下标从 0 开始还是从 1 开始,判定大小为 k 的“滑动窗口”中的元素个数>k 的条件,可统一为“滑动窗口”左端元素下标 ≤i-k。

【算法代码一:from 0 + 数组模拟】
队列头指针 h 初始化为 0,尾指针 t 初始化为 -1 时的写法。注意此时数组的下标从 0 开始。

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+5;
int a[maxn],q[maxn];
int n,k;

void minque() {
    int h=0,t=-1;
    for(int i=0; i<n; i++) { //from 0
        while(h<=t && q[h]<=i-k) h++;
        while(h<=t && a[q[t]]>a[i]) t--;
        q[++t]=i;
        if(i>=k-1) cout<<a[q[h]]<<" ";
    }
}

void maxque() {
    int h=0,t=-1;
    for(int i=0; i<n; i++) { //from 0
        while(h<=t && q[h]<=i-k) h++;
        while(h<=t && a[q[t]]<a[i]) t--;
        q[++t]=i;
        if(i>=k-1) cout<<a[q[h]]<<" ";
    }
}

int main() {
    cin>>n>>k;
    for(int i=0; i<n; i++) { //from 0
        cin>>a[i];
    }

    minque();
    cout<<endl;
    maxque();

    return 0;
}


/*
in:
8 3
1 3 -1 -3 5 3 6 7

out:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
*/


【算法代码二:from 1 + 数组模拟】
队列头指针 h 初始化为 1,尾指针 t 初始化为 0 时的写法。注意此时数组的下标从 1 开始。

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+5;
int a[maxn],q[maxn];
int n,k;

void minque() {
    int h=1,t=0;
    for(int i=1; i<=n; i++) { //from 1
        while(h<=t && q[h]<=i-k) h++; //q[h]<i-k+1
        while(h<=t && a[q[t]]>a[i]) t--;
        q[++t]=i;
        if(i>=k) cout<<a[q[h]]<<" ";
    }
}

void maxque() {
    int h=1,t=0;
    for(int i=1; i<=n; i++) { //from 1
        while(h<=t && q[h]<=i-k) h++; //q[h]<i-k+1
        while(h<=t && a[q[t]]<a[i]) t--;
        q[++t]=i;
        if(i>=k) cout<<a[q[h]]<<" ";
    }
}

int main() {
    cin>>n>>k;
    for(int i=1; i<=n; i++) { //from 1
        cin>>a[i];
    }

    minque();
    cout<<endl;
    maxque();

    return 0;
}


/*
in:
8 3
1 3 -1 -3 5 3 6 7

out:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
*/




【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/109680717
https://zhuanlan.zhihu.com/p/437438743
https://www.acwing.com/solution/content/6564/
https://www.acwing.com/file_system/file/content/whole/index/content/11525369/


 





   

更多推荐