给定一个大小为n≤106n≤106的数组。
有一个大小为k的滑动窗口,它从数组的最左边移动到最右边。
您只能在窗口中看到k个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为[1 3 -1 -3 5 3 6 7],k为3。
窗口位置 | 最小值 | 最大值 |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数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
这题我们可以使用数组模拟队列来进行求解,每次对queue维护,我们对这串数从左到右遍历,想象一个长度为k的窗口在上面不断的向右滑动~
而在这个窗口中(以最小值为例),我们每次入队一个数,如果有Ai>Aj && i<j,那么我们永远都不可能用到Ai,就将Ai出队(即弹出Aj前面所有比Aj大的数),那么我们所得的queue就是一个严格单调递增的队列,每次输出队首即可
我个人认为这段代码最难理解的是:当窗口划过时,如何去更新queue,可以这样理解,queue中的首项永远是我们窗口里面最小值的位置,如果我们遍历i的时候找到了比queue首项位置上更小的数,就将这个数的位置,更新成queue的首项,否则就一直在queue的队尾加数,而当我当前遍历到的位置i,i-k+1>q[hh],也就是说在极限情况时,q[hh]是窗口左边框,i是窗口的右边框,就需要将h++来维护队列.
具体操作:
1 3 -1 -3 5 3 6 7
第一步:将 1 入队,hh=0,q[0]=0;
第二步:1<3,将 3 入队, hh=0 ,q[0]=0,q[1]=1;
第三步:3>-1, 前面的数都出队,将 -1 入队, hh=0, q[0]=2, 输出a[2]=-1;
第四步:-1>-3, 前面的数都出队, 将-3入队,hh=0, q[0]=3, 输出a[3]=-3;
第五步:-3<5, 将 5 入队, hh=0, q[0]=3,q[1]=4, 输出a[3]=-3;
第六步:5>3, 将5出队, 3入队,hh=0, q[0]=3, q[1]=5, 输出a[3]=-3;
第七步:3<6,将6入队,此时i-k+1=4,hh++,hh=1,q[1]=5,q[2]=6, 输出a[5]=3;
第八步:6<7,将7入队, hh=1,hh=1,q[1]=5,q[2]=6,输出3;
#include <iostream> #include <cstdio> #include <cstring> #include <string.h> #include <math.h> #include <algorithm> #include <stack> #include <queue> #include <vector> #include <map> #include <set> #define ll long long const int N=1e6+10; using namespace std; typedef pair<int,int>PII; int n,k; int a[N],q[N]; int main(){ ios::sync_with_stdio(false); cin>>n>>k; for(int i=0;i<n;i++) cin>>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) cout<<a[q[hh]]<<‘ ‘; } return 0; }
原文地址:https://www.cnblogs.com/lr599909928/p/12325346.html