题目:
害,今天这题用自己的方法超时了,我自己的方法是暴力算法的优化版。即不是每次都去找最大值,而是暂存这个最大值和它的序号,然后再分别处理。将测试用例里面最大的那个单独处理之后,我自己的方法其实是能过的。
官方题解我看过之后,中间的双端队列感觉就是我这个方法的再次优化版本。所以我这里重点介绍这个方法
思路:
题目还是很好理解的,只是我们要考虑怎么让这个找寻的过程更快。
1.我们可以注意到,在窗口滑动的过程中,最大值很多时候是不变的。
所以就可以想到记录这个最大值和它对应的序号。
这样,就可以大幅度的节约每一次都去区间里寻找最大值的时间。然后,当它超过左边界或者是有更大的元素值时再进行处理。
2.答案则在这个想法上使用了双端队列进一步优化。对于一个队列,队列永远保持单调递减。然后,对于每一个右窗口遍历到的元素值,去比较,然后确定放置在队列的哪个位置。(小于右窗口值的元素可以永远弹出了,因为这时插入的右窗口值会后出队)
操作顺序是这样:
(1)首先,对于右窗口进来的元素n,与队列中的元素比较,不断弹出队列元素,直到遇到大于当前元素n的值停止。即保持队列的单调递减。
(2)然后,再判断左窗口是否越过了当前区间最大值的序号(队首序号)。如果越过,则弹出队首元素,让第二大的元素继承队首元素。
3.为了操作方便,题解存储的是序号而不是值,因为序号是需要做判断的,而值都可以由序号去检索,是一个一对一映射的关系。如果存储的是值,判断左窗口越过最大值序号这一步就会很困难。因为值不能一对一映射回序号。
C++代码(附带测试,还包括了我自己的算法,大家可以自行比较异同)
#include<iostream>
#include<vector>
#include<algorithm>
#include <deque>
using namespace std;
//行得通但是会超时
//class Solution {
//public:
// vector<int> maxSlidingWindow(vector<int>& nums, int k) {
// int i = 0;//左边窗口边界
// int j = k-1;//右边窗口边界
// int max = nums[0];//区间最大值
// int maxindex = 0;
// vector<int> ans;//答案数组
// for(int l = 0;l<k;l++)//初始化
// {
// if(nums[l]>max)
// {
// max = nums[l];
// maxindex = l;
// }
// }
//
// while(j<nums.size())
// {
// if(i==j)//窗口值为1,在这个算法里窗口值为1是特殊情况
// {
// maxindex = j;
// max = nums[j];
// }
//
// //通常情况
// if(i==maxindex+1)//弹出首元素了
// {
// maxindex = findmax(nums,i,j);//从i,j区间中找出下一个最大值标记号
// max = nums[maxindex];
// }
// if(nums[j]>max)
// {
// //如果右窗口进来的值大于当前最大值
// maxindex = j;
// max = nums[j];
// }
// ans.push_back(max);
// i++;
// j++;
// }
// return ans;
// }
// //寻找最大值标记函数
// int findmax(vector<int>& nums,int i,int j)
// {
// int max = nums[i];
// int maxindex = i;
// for(int l=i;l<j;l++)
// {
// if(nums[l]>max)
// {
// max = nums[l];
// maxindex = l;
// }
// }
// return maxindex;//返回最大值徐好
// }
//};
//题解算法
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
//注意该算法队列q存储的是元素的序号而不是具体值
int n = nums.size();
deque<int> q;//建立一个队列
for (int i = 0; i < k; ++i) {//初始化队列,让其具有窗口值
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}//保持队列的单调递减
q.push_back(i);//如果满足,则入队
}
vector<int> ans = {nums[q.front()]};//将第一个元素放入答案数组
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}//保持队列单调性
q.push_back(i);//当前元素入列
while (q.front() <= i - k) {
q.pop_front();
}//当最大值元素超过了窗口左边界,弹出第一个元素即可
ans.push_back(nums[q.front()]);//每一次都压入第一个元素
}
return ans;
}
};
int main()
{
vector<int> nums = {1,3,-1,-3,5,3,6,7};
int k = 3;
Solution s;
vector<int> ans = s.maxSlidingWindow(nums,k);
for(auto& a:ans)
{
cout<<a<<endl;
}
}