题目描述
解法一:暴力法
最简单直接的方法是遍历每个滑动窗口,找到每个窗口的最大值。假设数组的长度为n,则一共有 n - k + 1 个滑动窗口,每个滑窗有 k 个元素,遍历 n - k + 1次,每次从 k个数中寻找最大值,于是算法的时间复杂度为 O(n*k),表现较差。
Java
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0 || nums.length < k || k == 0) return new int[0];
int[] res = new int[nums.length - k + 1];
for(int i=0; i<nums.length - k + 1; i++) {
res[i] = Arrays.stream(nums, i, i+k).max().getAsInt(); // 获取当前子数组的最大值
}
return res;
}
}
Python
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
res = []
for i in range(len(nums)-k+1):
res.append(max(nums[i:i+k]))
return res
解法二:设计单调双端队列
Java
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0 || k == 0) return new int[0];
Deque<Integer> deque = new LinkedList<> ();
int[] res = new int[nums.length-k+1];
for(int i=0; i<k; i++) { // 未形成窗口
// 每次添加新元素前,删除当前 deque内所有小于该元素的值,以保持 deque非严格递减,队首始终存放队列中最大元素,
// 因为比新加入元素更小的元素不可能会成为窗口最大值,直接剔除即可。
while(!deque.isEmpty() && deque.peekLast()<nums[i])
deque.removeLast();
deque.addLast(nums[i]); // 加入新元素
}
res[0] = deque.peekFirst(); // 当形成首个窗口后,此时队首即为窗口最大值
for(int j=k; j<nums.length; j++) { // 窗口滑动
if(deque.peekFirst() == nums[j-k]) // 每轮窗口滑动会移除元素nums[j-k],需要将deque中对应元素一起删除
deque.removeFirst(); // 若此时队首元素恰好是此轮为移出窗口的元素,则队首元素出队。
while(!deque.isEmpty() && deque.peekLast()<nums[j]) // 保持队首始终存放的是队列中最大元素
deque.removeLast();
deque.addLast(nums[j]);
res[j-k+1] = deque.peekFirst(); // 队首即为此轮窗口内最大值
}
return res;
}
}
另一种写法,集成到一个 for循环中
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0 || nums.length < k || k == 0) return new int[0];
Deque<Integer> deque = new LinkedList<> ();
int[] res = new int[nums.length - k + 1];
for(int i=0; i<nums.length; i++) {
// 当遍历过的元素超过 k 个,即产生首个窗口后,才开始判断队列中是否存在已经滑出窗口的对应元素,有则剔除
if(i>=k && deque.peekFirst() == nums[i-k]) deque.removeFirst();
// 始终保持队首存放当前窗口内最大元素,注意不能写成 <= nums[i],因为有可能存在多个重复的值均为当前最大值,
// 后面删除时,也是先删除下标靠前的,后面重复的元素保留
while(!deque.isEmpty() && deque.peekLast() < nums[i])
deque.removeLast();
deque.addLast(nums[i]); // 当前窗口右边界滑过的新元素入队
if(i>=k-1) res[i-k+1] = deque.peekFirst(); // 当滑窗滑过的元素超过 k个时,才开始判断第一个窗口最大值,每轮窗口最大值即为当前队首元素
}
return res;
}
}
Python
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#----采用双端队列-----
win = [] # 滑动窗口,注意:里头保存的是索引值
res = []
for i, v in enumerate(nums):
if i >= k and win[0] == i-k: # 当有元素从滑窗左边界滑出的时候,如果它恰好是窗口内的最大值(位于队首 win[0]),将它弹出
win.pop(0)
while win and nums[win[-1]] <= v: # 如果滑动窗口非空,新进来的数比队列里某些已经存在的数还要大时
win.pop() # 则说明这些已经存在的数不可能成为滑动窗口的最大值(它们永无出头之日),将它们全部弹出
win.append(i) # 将每个元素的索引存放进 win中
if i >= k-1: # 当滑窗滑过的元素超过 k个时,才开始判断第一个最大值
res.append(nums[win[0]]) # 队首是滑动窗口内最大值的索引
return res