滑动窗口解决什么问题?
1. 需要输出或比较的结果在原数据结构中是连续排列的;
2. 每次窗口滑动时,只需观察窗口两端元素的变化,无论窗口多长,每次只操作两个头尾元素,当用到的窗口比较长时,可以显著减少操作次数;
3. 窗口内元素的整体性比较强,窗口滑动可以只通过操作头尾两个位置的变化实现,但对比结果时往往要用到窗口中所有元素。
滑动窗口、尺取法关系?
尺取法就是双指针(two points)嘛。而滑动窗口也要用到二个指针,所以滑动窗口是一种特殊的尺取法。
经典例题:
leetcode 239. 滑动窗口最大值
题意:
在一个数组中找长度为k的子序列 中的最大值,如下图所示:
滑动窗口的位置 最大值
--------------- -----
[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的子序列,说明窗口的大小是固定的,且为k。
找每个窗口中的最大值,我们可以遍历窗口中的所有值,从而找到最大值,此时,时间复杂度为O(n * m)
用单调双端队列找最大值可以使时间复杂度变为O(n)。
下面用的单调双端队列 是单调递减的。那么队列的第一个元素即为最大值,而且,队列前面的数 一定 比队列后面的数放入时间早。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
if (nums.size() <= 0 || k < 1 || k > nums.size()){
return res;
}
deque<int> list;//创建双端队列
for (int i = 0; i < nums.size(); i++){ //i表示窗口的结尾下标
while (list.size() != 0 && nums[*(list.end() - 1)] <= nums[i]){
list.pop_back();
}//while
list.push_back(i);
if (*list.begin() == i - k){
list.pop_front();
}//if
if (i >= k - 1){ //i 要到 k - 1位置处才是一个完整的窗口
res.push_back(nums[*list.begin()]);
}//if
}//for
return res;
}
};
<