题目传送门:239. 滑动窗口最大值 - 力扣(LeetCode)
题意就是求每个窗口内的最大值,返回一个最大值的数组,滑动窗口的最值问题。
做法:维护一个单调递减队列,队头为当前窗口的最大值。
设计的单调队列在入队、出队操作时满足如下规则:
- 入队:新元素入队前,如果队尾元素大于要插入的新元素,那么就出队。就这样移除掉所有比插入的元素小的值。
- 出队:检查队头元素是否超出窗口范围(下标 <= i - k),若超出则移除。
每次窗口形成后,队头元素即位窗口的最大值。
注:
- 存储下标而非值,便于判断元素是否过期(该元素是否还在窗口中,窗口移动时需移除左边元素,i - k即为当前窗口的最左边元素下标)。
- 结果数组长度 = 窗口滑动次数 = n - k + 1。n - k理解为减去刚开始的窗口元素还有n-k的元素需要进窗口,然后再加上第一次的窗口(没移动,也可以理解为窗口刚移动到数组)。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length < k || k <= 0) {
return new int[0];
}
int n = nums.length;
int[] res = new int[n - k + 1];
Deque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!q.isEmpty() && q.peekFirst() <= i - k) {
q.pollFirst();
}
while (!q.isEmpty() && nums[q.peekLast()] < nums[i]) {
q.pollLast();
}
q.offerLast(i);
if (i >= k - 1) {
res[i - k + 1] = nums[q.peekFirst()];
}
}
return res;
}
}
以 nums = [1,3,-1,-3,5], k=3
为例,算法的执行流程为:
当前元素 | 窗口 | 单调递减队列(下标) | 最大值 | 操作说明 |
---|---|---|---|---|
i = 0 nums[0] = 1 | [1] | [0] | - | 初始入队 |
i = 1 nums[1] = 3 | [1, 3] | [1] | - | 移除0(1 < 3),1入队 |
i = 2 nums[2] = -1 | [1, 3, -1] | [1, 2] | 3 | 2入队,记录队头nums[1] = 3 |
i = 3 nums[3] = -3 | [3, -1, -3] | [1, 2, 3] | 3 | 3入队,队头未过期 |
i = 4 nums[4] = 5 | [-1, -3, 5] | [4] | 5 | 移除比5小的 |