目录
滑动窗口
要求:
总时间限制: 12000ms
内存限制: 65536kB
描述:
给定一个长度为n(n<=10^6)的数组。有一个大小为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
思路分析:
此题可以说是关于队列的题目中的一个经典难题了. 为了获得最值,一个平凡的想法是利用priority_queue来进行存储,priority_queue存储pair<int,int>,前者是数组中的值,后者是值在数组中的位置,每次push之后的数,再在适当的时候剔除掉前面滑动窗口滑过的数.
***注意:我们不需要存储原有的数组,只需要每次把下一个数scanf就可以了,这样可以省一些空间(此题对空间的要求也是有的)
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
struct Pair{
int first;
int second;
Pair(int a,int b):first(a),second(b){}
bool operator< (const Pair& y)const{
if(first == y.first) return second < y.second;
else return first < y.first;
}
bool operator> (const Pair& y)const{
return y<(*this);
}
};
int main(){
int n,k;
scanf("%d%d",&n,&k);
priority_queue<Pair,vector<Pair>,less<Pair> > q1;
priority_queue<Pair,vector<Pair>,greater<Pair> > q2;
int num;
int Ans[n-k+1];
for(int i=0;i<k;i++){
scanf("%d",&num);
q1.push(Pair(num,i));
q2.push(Pair(num,i));
}
Ans[0]=q1.top().first;
printf("%d",q2.top().first);
for(int i=k;i<n;i++){
scanf("%d",&num);
q1.push(Pair(num,i));
q2.push(Pair(num,i));
while(q1.top().second<=i-k){
q1.pop();
}
while(q2.top().second <=i-k){
q2.pop();
}
Ans[i-k+1]=q1.top().first;
printf(" %d",q2.top().first);
}
printf("\n%d",Ans[0]);
for(int i=1;i<=n-k;i++){
printf(" %d",Ans[i]);
}
printf("\n");
}
这样的结果可以通过,但是时间接近300ms,我们还可以进一步优化,也就是利用单调队列的思路.
我们利用deque双端队列,手动将它实现成一个单调的队列,其余思路基本相同,这样时间是比优先队列的堆排序会更快的.
以递增队列为例,我们通过递增队列来获得最小值,对于每次新接受的值,我们将其与队列中原有的最后一个值比较,如果最后一个值更大,就把它从队列中剔除,而保留新接受的值.
最终代码:
#include<iostream>
#include<deque>
using namespace std;
typedef pair<int,int> Pair;
int main(){
int n,k;
scanf("%d%d",&n,&k);
deque<pair<int,int> > q1,q2;
const int maxn = 1e6+1;
int num;
int Ans1[maxn];
int Ans2[maxn];
for(int i=0;i<k;i++){
scanf("%d",&num);
/* 保持单调性的操作 */
while(!q1.empty()&&num<q1.back().first){
q1.pop_back();
}
while(!q2.empty()&&num>q2.back().first){
q2.pop_back();
}
q1.push_back(Pair(num,i));
q2.push_back(Pair(num,i));
}
Ans1[0]=q1.front().first;
Ans2[0]=q2.front().first;
for(int i=k;i<n;i++){
scanf("%d",&num);
/* 保持单调性的操作 */
while(!q1.empty()&&num<q1.back().first){
q1.pop_back();
}
while(!q2.empty()&&num>q2.back().first){
q2.pop_back();
}
q1.push_back(Pair(num,i));
q2.push_back(Pair(num,i));
while(q1.front().second<=i-k){
q1.pop_front();
}
while(q2.front().second<=i-k){
q2.pop_front();
}
Ans1[i-k+1]=q1.front().first;
Ans2[i-k+1]=q2.front().first;
}
printf("%d",Ans1[0]);
for(int i=1;i<=n-k;i++){
printf(" %d",Ans1[i]);
}
printf("\n%d",Ans2[0]);
for(int i=1;i<=n-k;i++){
printf(" %d",Ans2[i]);
}
printf("\n");
}