题目:给定一个数组 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
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
解题思路:
设窗口区间为 [i, j] ,最大值为 xj 。当窗口向前移动一格,则区间变为[i + 1, j + 1],即添加了nums[j + 1], 删除了nums[i]。
若只向窗口[i, j] 右边添加数字nums[j + 1],则新窗口最大值可以通过一次对比 使用O(1)时间得到, 即:
xj + 1 = max(xj,nums[j + 1])
而由于删除的nums[i]可能恰好是窗口内唯一的最大值xj,因此不能通过以上方法计算xj + 1,而必须使用O(j - i)时间,遍历整个窗口区间获取最大值,即:
xj+1=max(nums(i+1),⋯,num(j+1))
根据以上分析,可得 暴力法 的时间复杂度为 O(nk) 。
- 设数组 nums 的长度为 n,则共有 (n-k+1) 个窗口;
- 获取每个窗口最大值需线性遍历,时间复杂度为 O(k)。
算法流程:
- 初始化: 双端队列 deque ,结果列表 res ,数组长度 n ;
- 滑动窗口: 左边界范围 i∈[1−k,n−k] ,右边界范围 j ∈[0,n−1] ;
1、若 i > 0 且 队首元素 deque[0] == 被删除元素 nums[i - 1] :则队首元素出队;
2、删除 deque 内所有 < nums[j] 的元素,以保持 deque 递减;
3、将 nums[j]添加至 deque 尾部;
4、若已形成窗口(即 i ≥ 0 ):将窗口最大值(即队首元素 deque[0] )添加至列表 res;
3. 返回值: 返回结果列表 res;
package Algriothm; import java.util.Arrays; import java.util.Deque; import java.util.LinkedList; public class SlidingWindow { public static void main(String[] args) { System.out.println(Arrays.toString(maxSlidingWindow(new int[]{1, 3, -1, -3, 5, 3, 6, 7}, 3)));; } public static 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++) { while (!deque.isEmpty() && deque.peekLast() < nums[i]) { deque.removeLast(); } deque.addLast(nums[i]); } res[0] = deque.peekFirst(); for (int i = k; i < nums.length; i++) { if (deque.peekFirst() == nums[i - k]) { deque.removeFirst(); } while (!deque.isEmpty() && deque.peekLast() < nums[i]) { deque.removeLast(); } deque.addLast(nums[i]); res[i - k + 1] = deque.peekFirst(); } return res; } }