目录
一、题目描述
给定一个数组 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
提示:
- 1 <= nums.length <= 10^5
- -10^4 <= nums[i] <= 10^4
- 1 <= k <= nums.length
二、解题思路
题意很清晰,可以想到,加入任意时刻窗口的最大值为x,新加入进来的数为y,如果y>x,那么更新最大值为y,否则最大值不变;然后在窗口往后移动,比较新加入的数与当前窗口的最大值。这样的想法是没错的,但是有一种情况要考虑到,那就是如果当前窗口的最大值在窗口的最左边,而且新加进来的数比当前窗口的最大值小,那在求窗口最大值时要先移动一格窗口,最大值就被移除了,此时的最大值应该为窗口内现有的数与新加进来的数进行比较。所以按照这种思路,这道题是解不出正确答案的。
- 需求是:需要一个能求O(1)时间找出当前窗口的最大值,并且O(1)时间移除元素的数据结构,有没有这样的结构呢?
这里就需要经验了,满足要求的直接的数据结构没有,但是利用双端队列然后维护一个单调性就有了。
- 维护队列的单调性,保持队列元素单调递减,那么保证队列的最大值始终在队列开头,双端队列O(1)时间取数;
- 当最大值在窗口最左边需要移出时,从队列左端移出,移出时间O(1)。
如此一来,这道题就能解了。
剑指offer这道题为简单题,力扣主站题库标记为困难的???就离谱……
三、代码实现
#include <bits/stdc++.h>
using namespace std;
//滑窗+单调(双端)队列
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int length = nums.size();
if (length == 0) return {};
int right = 0, left = 0;
vector<int> res;
//单调递减队列
deque<int> dq;
while (right < length) {
while (!dq.empty() && nums[right] > dq.back()) {
dq.pop_back();
}
dq.push_back(nums[right]);
right++;
if (right - left >= k) {
res.push_back(dq.front());
//当窗口最左边的数正好是最大值时,需要移出窗口
if (nums[left] == dq.front()) {
dq.pop_front();
}
left++;
}
}
return res;
}
int main() {
vector<int> arr = { 1, 3, -1, -3, 5, 3, 6, 7 };
arr = maxSlidingWindow(arr, 3);
for (int i = 0; i < arr.size(); i++) {
cout << arr[i] << " ";
}
return 0;
}