239. Sliding Window Maximum
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.
Example:
Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[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
Note:
You may assume k is always valid, 1 ≤ k ≤ input array’s size for non-empty array.
Follow up:
Could you solve it in linear time?
Hint:
- How about using a data structure such as deque (double-ended queue)?
- The queue size need not be the same as the window’s size.
- Remove redundant elements and the queue should store only elements that need to be considered.
方法0: brute force
Complexity
Time complexity: O((n - k + 1)n), which is O(n2) at its worst case k = n / 2
Space complexity: O(1)
Iterator:
// Author: Huahua
// Running time: 180 ms
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ans;
for (int i = k - 1; i < nums.size(); ++i) {
ans.push_back(*max_element(nums.begin() + i - k + 1, nums.begin() + i + 1));
}
return ans;
}
};
Index:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if (nums.empty()) return {};
int n = nums.size();
vector<int> result;
for (int i = k - 1; i < n; i++){
int mx = INT_MIN;
for (int j = 0; j < k; j++){
mx = max(mx, nums[i - j]);
}
result.push_back(mx);
}
return result;
}
};
方法1: deque
思路:
vector只支持pop_back, push_back, 不支持pop_front, push_front, 而deque同时支持前后。之前从vector前面删除是利用iterator,代价也比较高。deque的特点包括:
- 只是多了从前面插入和删除,并且O(1),其他interface几乎一样
- 在内存里不一定是连续储存
- https://www.geeksforgeeks.org/difference-between-stdremove-and-vectorerase-for-vectors/.
下面这个方法在deque中存入的实际是每个数的坐标,这样能使deque中的数和nums中的对应起来。“It’s more handy to store in the deque indexes instead of elements since both are used during an array parsing”. 每当进队一个数需要如下逻辑:1. 队列是否为空,是的话可以直接进队,2. 如果对列不为空,取将q中所有比nums[i] 小的数字从后往前pop_back(),3,检查front是否已经过期:q.front == i - k,因为此时的sliding window只包含 i - k + 1 到 i, 4. nums[i] 入栈push_back, 5. 如果在 i - k + 1 >= 0 的范围内,此时可以推入q.front()作为结果。
官方题解:
易错点
- 第三步的检查不太在乎顺序,只要保证过期元素不会被最后一步推进结果
- 先pop_back()和push_back的顺序必须遵守
Complexity
Time complexity: O(n)
Space complexity: O(n)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if (nums.empty()) return {};
int n = nums.size();
vector<int> result;
deque<int> q;
for (int i = 0; i < n; i++){
if (!q.empty() && q.front() == i - k){
q.pop_front();
}
while (!q.empty() && nums[q.back()] < nums[i]){
q.pop_back();
}
q.push_back(i);
if (i - k + 1 >= 0) {
result.push_back(nums[q.front()]);
}
}
return result;
}
};
// [1,3,-1,-3,5,3,6,7]
二刷:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> mq;
vector<int> res;
for (int i = 0; i < nums.size(); i++) {
while (!mq.empty() && nums[mq.back()] < nums[i]) mq.pop_back();
while (!mq.empty() && mq.front() <= i - k) mq.pop_front();
mq.push_back(i);
if (i >= k - 1) res.push_back(nums[mq.front()]);
}
return res;
}
};
方法2: heap
discussion: https://leetcode.com/problems/sliding-window-maximum/discuss/65999/3-C%2B%2B-Solutions
思路:
用multiset保持k个元素sorted。
易错点
Complexity
Time complexity: O(nlogk)
Space complexity: O(k)
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
if (k == 0) return result;
multiset<int> w;
for (int i = 0, n = (int)nums.size(); i < n; i++) {
if (i >= k)
w.erase(w.find(nums[i-k]));
w.insert(nums[i]);
if (i >= k-1)
result.push_back(*w.rbegin());
}
return result;
}
方法3: dynamic programming
官方题解:https://leetcode.com/problems/sliding-window-maximum/solution/