算法练习(六):滑动窗口的最大值
题目:给定一个数组 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;
}
}
浙公网安备 33010602011771号