【算法题】滑动窗口求最值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 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 31 [3 -1 -3] 5 3 6 7 31 3 [-1 -3 5] 3 6 7 51 3 -1 [-3 5 3] 6 7 51 3 -1 -3 [5 3 6] 7 61 3 -1 -3 5 [3 6 7] 7
方法思路
- 单调队列的维护:使用一个双端队列(
Deque),队列中存储的是数组元素的索引。队列中的元素对应的数组值是单调递减的。 - 处理滑动窗口的移动:
- 当滑动窗口向右移动时,新元素进入窗口。如果队列尾部元素对应的数组值小于等于当前新元素的值,就将队列尾部元素弹出,直到队列尾部元素对应的数组值大于当前新元素的值,再将当前新元素的索引加入队列尾部。这样保证了队列的单调递减性。
- 同时,要检查队列头部的元素是否已经不在当前滑动窗口内(即索引小于当前窗口的左边界),如果是,就将队列头部元素弹出。
- 获取窗口最大值:当滑动窗口的右边界移动到至少
k - 1的位置时,队列头部的元素对应的数组值就是当前滑动窗口的最大值,将其加入结果列表。
import java.util.ArrayDeque;
import java.util.Deque;public class SlidingWindowMaximum {public int[] maxSlidingWindow(int[] nums, int k) {if (nums == null || nums.length == 0 || k <= 0) {return new int[0];}int n = nums.length;int[] result = new int[n - k + 1];int index = 0;Deque<Integer> deque = new ArrayDeque<>();for (int i = 0; i < n; i++) {// 维护队列的单调递减性,弹出队列尾部小于等于当前元素的索引while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) {deque.pollLast();}// 将当前元素索引加入队列尾部deque.offerLast(i);// 检查队列头部元素是否在窗口外if (deque.peekFirst() == i - k) {deque.pollFirst();}// k - 1后才形成窗口,此时记录最大值if (i >= k - 1) {result[index++] = nums[deque.peekFirst()];}}return result;}public static void main(String[] args) {SlidingWindowMaximum solution = new SlidingWindowMaximum();int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};int k = 3;int[] result = solution.maxSlidingWindow(nums, k);for (int num : result) {System.out.print(num + " ");}}
}
