题目描述:
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。
返回滑动窗口最大值。
示例:
输入: nums =[1,3,-1,-3,5,3,6,7]
, 和 k = 3 输出:[3,3,5,5,6,7] 解释:
滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
注意:
你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。
进阶:
你能在线性时间复杂度内解决此题吗?
思路:
这道题本来我使用的是O(N^2)的方法,后来学习了LeetCode上别人的解法,在此记录一下。
这个方法使用了一个双端队列来保存来保存窗口中的数据的index,对这个队列的具体操作如下【虽然队列中保存的是index,但是为了方便描述,就直接描述为数据】:
1. 在插入新数据的时候,如果队列为空就直接插入
2. 如果队列不为空就判断新数据和队尾元素的大小,如果队尾元素小于新数据,那么我们可以确定,在包含队尾元素和新数据的窗口里面,一定是新数据最大,有了这个假设,我们就可以把队尾元素移除队列,然后重复这个比较,直到遇到一个比新数据大的数才停止出队列,然后再把新数据插入。
这个就保证了,在队首的数据一定是当前窗口中最大的,然后队首后面的是次大的数据,依次类推,我们只需要在每次更新窗口的时候【因为只有更新窗口才会有新数据插入】把队首元素提取出来,即可得到结果
代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(nums.size() == 0 || k <= 0 || k > nums.size()) return {};
vector<int> ret;
deque<int> dq;
int i = 0;
for(; i < k; ++i){
while(!dq.empty() && nums[dq.back()] < nums[i])
dq.pop_back();
dq.push_back(i);
}
ret.push_back(nums[dq.front()]);
while(i < nums.size()){
//判断队头的值是否已经不在滑动窗口中了
if(i - dq.front() >= k) dq.pop_front();
while(!dq.empty() && nums[dq.back()] < nums[i])
dq.pop_back();
dq.push_back(i);
i++;
ret.push_back(nums[dq.front()]);
}
return ret;
}
};