题目链接:Leetcode
tag: 队列 双端队列 滑动窗口
难易程度:困难
题目描述:
给定一个数组 nums 和滑动窗口的大小 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. 你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。 解题思路本题难点
数组无序,窗口内的最大值。
具体思路
双向队列:在头部进行插入、删除操作,也能在尾部进行插入删除操作。
单调的双向队列:从队列的头部到尾部,所存储的元素是依次递减(或依次递增)的。
我们维护一个单调的双向队列,窗口在每次滑动的时候,我就从队列头部取当前窗口中的最大值,每次窗口新进来一个元素的时候,我就将它与队列中的元素进行大小比较:
进来的元素 > 队列的尾部元素:那么先将队列尾部的元素弹出,然后把刚刚进来的元素添到队列的尾部;
进来的元素 < 队列的尾部元素:那么把刚刚进来的元素直接添到队列的尾部即可。
删除元素:当队列的大小超过窗口的大小时,将队列的最大值弹出,窗口进行滑动。
代码 class Solution { public int[] maxSlidingWindow(int[] nums, int k) { if(nums == null || k == 0){ return new int[0]; } Deque<Integer> deque = new LinkedList<>(); int[] res = new int[nums.length - k + 1]; int count = 0; for(int i = 0; i < nums.length; i++){ // 在队列不为空的情况下,如果队列尾部的元素要比当前的元素小,或等于当前的元素 // 那么为了维持从大到小的原则,我必须让尾部元素弹出 while(!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]){ deque.pollLast(); } // 不走 while 的话,说明我们正常在队列尾部添加元素 deque.add(i); // 如果滑动窗口已经略过了队列中头部的元素,则将头部元素弹出 if(deque.peekFirst() == (i - k)){ deque.pollFirst(); } // 看看窗口有没有形成,只有形成了大小为 k 的窗口,我才能收集窗口内的最大值 if(i >= k-1){ res[count++] = nums[deque.peekFirst()]; } } return res; } }复杂度分析:
时间复杂度 O(N) : 其中 n 为数组 nums 长度;线性遍历 nums 占用 O(N) ;每个元素最多仅入队和出队一次,因此单调队列 deque 占用 O(2N) 。
空间复杂度 O(k) : 双端队列 deque 中最多同时存储 k个元素(即窗口大小)。
其他优秀解答解题思路
改善之后的暴力法
代码
public int[] maxSlidingWindow(int[] nums, int k) { int len = nums.length; if (len == 0){ return new int[0]; } //定义结果数组 int[] res = new int[len - k + 1]; //maxInd记录每次最大值的下标,max记录最大值 int maxInd = -1, max = Integer.MIN_VALUE; for (int i = 0; i < len - k + 1; i++) { //判断最大值下标是否在滑动窗口的范围内 if (maxInd >= i){ //存在就只需要比较最后面的值是否大于上一个窗口最大值 if (nums[i + k - 1] > max){ max = nums[i + k - 1]; //更新最大值下标 maxInd = i + k - 1; } } //如果不在就重新寻找当前窗口最大值 else { max = nums[i]; for (int j = i; j < i + k; j++) { if (max < nums[j]) { max = nums[j]; maxInd = j; } } } res[i] = max; } return res; }