单调队列-滑动窗口最大值
介绍
单调队列的经典用法:维持滑动窗口在滑动中的最大值(从队尾到队头单调递增)。队列中的元素随着滑动窗口的移动都有成为最大值的可能性,所以需要该队列暂时保存。
单调递增队列实现代码
public static class MyQueue {
public ArrayDeque<Integer> deque = new ArrayDeque<>();
private int[] nums;
public MyQueue(int[] nums) {
this.nums = nums;
}
public Integer poll(int index) {//滑动窗口左边界
if (deque.isEmpty() || deque.peekFirst() != index) {//过期判断,没过期
return -1;
}
return deque.pollFirst();//过期了
}
public Integer max() {
return deque.peekFirst();
}
public void offer(int ele) {
while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[ele]) {
deque.pollLast();
}
deque.offerLast(ele);
}
}
题目:滑动窗口最大值
描述 239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k **的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
分析
- 看到这里的初始想法,可以使用优先队列来完成,随着窗口的滑动,可以先移除左窗口的元素,再把右窗口的元素加入到优先队列中。该做法放到leetcode上超时了。优先队列移除一个元素和加入一个元素的代价都是O(logN)级别的。总体就是O(NlogN)。
- 正确解法:单调队列。在滑动过程中,维持一个单调递增队列,每次取队列头部元素即为当前窗口的最大值。总体代价为O(N)。
代码实现
public int[] maxSlidingWindow(int[] nums, int k) {
MyQueue maxQueue = new MyQueue(nums);
for (int i = 0; i < k - 1; i++) {//入队前k-1个元素
maxQueue.offer(i);
}
int resSize = nums.length - k + 1;
int[] res = new int[resSize];//结果记录
int left = 0;
int right = k;//左闭右开
for (int i = 0; i < resSize; i++) {
maxQueue.offer((right - 1));//加入队列
right++;
res[i] = nums[maxQueue.max()];
maxQueue.poll(left++);//移除左窗口
}
return res;
}
总结
单调队列的经典用法,用于获取滑动窗口在滑动过程中的最大值。