题目
思路
对于一个数组区间的最值,可以开辟一个队列记录(当然这里不能叫队列只是和队列相似,习惯性叫法)。
每个区间的最值等于队首元素。扫描数组时,如果该元素大于队尾元素(取最大值时)将该队尾元素出队,直到队尾元素大小小于该元素停止出队。将数组元素加入队尾(无论该元素是否大于队尾元素都要入队)。如果队首元素不在该区间就出队。当前队首元素就是最值。
上面的方法基于模板题题解,下面给出我的理解(以求最大值为例)。
在队列中元素都是在给定区间的。因此如果新加入的元素大于队尾元素,那么该队尾元素肯定不是当前区间和接下来区间的最大值,因为新加入的元素已经是当前区间的成员了而且新加入元素索引大于队尾元素也不会是接来下一段区间的最值。如果其值优于队尾元素,那么队尾元素肯定不是当前和接下来一段区间的最值,那么该元素就不能被输出,因此队尾元素需要出队。如果该元素是当前区间的最值,那么队列会全部出队,新的元素会成为新的队首。因此需要队列一直出队,直到队尾元素大于当前元素。
队首元素就是当前队列的最大值,因为每次添加新元素会将队尾元素出队。队首元素出队除了被新元素出队,还有一种请况就是索引不在前范围,也会被出队。
这样就动态地维护一段最值。这里使用的是deque头文件。因为传统的队列是无法队尾出队。而vector效率相对较低,因此采用deque
代码
#include<iostream>
#include<bits/stdc++.h>
#include<deque>
using namespace std;
long int a[1000000];
deque<long int> mins;
deque<long int> maxs;
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++){
if(i>=m&&mins.front()==a[i-m]) //如果队首元素不在范围内
mins.pop_front();
while(mins.size()>0&&mins.back()>a[i]){//如果队尾元素大于新元素就出队。因为这里是求的最小值。
mins.pop_back();
}
mins.push_back(a[i]);//添加新元素
if(i>=m-1){//输出队首元素
cout<<mins.front();
if(i<n-1)
cout<<' ';
}
}
cout<<endl;
for(int i=0;i<n;i++){ //这里求的最大值,和上面相似。
if(i>=m&&maxs.front()==a[i-m])
maxs.pop_front();
while(maxs.size()>0&&maxs.back()<a[i]){
maxs.pop_back();
}
maxs.push_back(a[i]);
if(i>=m-1){
cout<<maxs.front();
if(i<n-1)
cout<<' ';
}
}
}