一: 问题介绍
二: 问题分析——分析最大值举例
1. 一般求解
毫无疑问的,按照我们的传统思维暴力解决这一类问题,我们可以直接对k大小的窗口进行一个扫描和记录即可得到窗口内的最大值或者最小值的结果。但其时间复杂度往往是我们不可以接受的,因为每次扫描k次,若一共进行n次扫描,这个方法的时间复杂度是n*k的,在最坏的情况下这个时间复杂度可能达到O(n * n) ,这就是最常规的暴力求解思路。
2. 分析优化
a. 分析:
我们首先直接选取一个一般的情况来分析找规律。假设窗口内的数字此时为 [ 5 7 3 4 6 2 8 1 3]. 假设我们此时要找窗口内的最大值。
此时最大值假设为指针 j 指向,可以发现最大值会指向数字 8 . 我们可以发现找最大值的时候我们第一次会扫描 5 7 3 4 6 2 8,第二次当窗口右移的时候又会重复扫描 7 3 4 6 2 8 得到这一段的最大值为8 ,其实这就已经导致重复扫描了,于是我们想到能不能将重复扫描的部分直接剔除用最大数字来代表这一段。
结论:当队列当中存在 i < j 并且 num[ i ] <num[ j ]的时候,可以发现只要 i 在窗口内的时候 j 也一定在窗口当中,并且 num[ j ]的值永远可以替代掉num[ i ]的值,即num[i] 其实并不需要进行保存。
b. 优化:
因此,在窗口当中我们只要保存一个单调递减的数列即可,因为如果后面的数字比前面的数字更大的话,那么前面的数字可以直接删去由后面的数字进行代替。
三: 代码求解
1. 代码解析
a. 保存数列方式:使用deque< int >处理
原因很简单,因为这个双头队列可以方便我们插入的时候可以快速利用pop_back()处理前面比我们小的数字。当窗口右移的时候也可以如果需要也可以使用pop_front()快速删除前面移出窗口的数字。
b. 数字入队列代码:
保证单调下降队列,所以先清空前面比我小的数字,再将入队列的数字进行插入。
while(!q.empty()&&a[i]>q.back()) q.pop_back(); //先删除前面更小的数字,保证单调递减
q.push_back(a[i]); // 再将当前数字加入队列
c. 数字出队列代码:
当窗口向右移动的时候,会有数字被移出窗口内,此时我们就需要判断移出的数字是否再窗口内,如果在我们就pop_front(),移出这个数字。
//i 代表当前加入的数字的序号 ,a[]代表数字序列
//当i大于K的时候,窗口最前面的数字就会被移出窗口此时就需要进行判断
if(i>=k+1) if(q.front()==a[i-k]) q.pop_front();
d. 答案:最大值就为单调队列里面的第一个数字了,
例如上面最后优化的结果 5 7 3 4 6 2 8 1 3 被优化为 8 3,最大值也就显然为8了。因为8前面数字都不会比它更大,而后面的数字进入的时候没有把8移除证明后面的数字也不会有8大,于是当前窗口内的最大值就是第一个数字了。
// i>=k :首先窗口要进入k个数字后才能再输出结果
if(i>=k) cout<<q.front()<<" ";
2. 完整代码+运算结果
a. 完整代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int N=1e6+10;
deque<int> q;
int a[N];
int main(){
int n,k;
cin>>n>>k; // 输入 8 3
for(int i=1;i<=n;i++) cin>>a[i];// 1 3 -1 -3 5 3 6 7
cout<<"最小值结果为:";
// 找窗口内的最小值,原理相似保证单调上升队列即可
for(int i=1;i<=n;i++){
//保证单调上升
while(!q.empty()&&a[i]<q.back()) q.pop_back();
q.push_back(a[i]);
if(i>=k+1) if(q.front()==a[i-k]) q.pop_front();
if(i>=k) cout<<q.front()<<" ";
}
puts("");
q.clear();
cout<<"最大值结果为:";
// 找窗口内的最大值
for(int i=1;i<=n;i++){
while(!q.empty()&&a[i]>q.back()) q.pop_back();
q.push_back(a[i]);
if(i>=k+1) if(q.front()==a[i-k]) q.pop_front();
if(i>=k) cout<<q.front()<<" ";
}
}
b. 运行结果: