单调队列题目
239.滑动窗口最大值
题目描述
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入: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
示例 2:
输入:nums = [1], k = 1
输出:[1]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
基本思路
我们需要维护一个单调递减的双端队列, 队列中存放元素的下标, 这样, 队头元素是当前窗口的最大值
![image-20221012190417954](https://i-blog.csdnimg.cn/blog_migrate/2f516fcee2093ab1a171053e2ffed521.png)
另外我们需要保证移除时, 移除过期元素和冗余元素 :
过期元素 :
![image-20221012190503065](https://i-blog.csdnimg.cn/blog_migrate/1a7136fb3209af1b76d9e1e2aa1a0ea0.png)
冗余元素 :
![image-20221012190525658](https://i-blog.csdnimg.cn/blog_migrate/35cc26d257befef7518199980c458c8b.png)
思路图解
完整流程如下 :
队头元素是当前窗口的最大值 :
如上图 :
- 白色的块是被移除的元素
基本思路是 :
- 遍历 nums 数组, 将元素放入数组中, 等到 i >= k -1 (第一个窗口尾部元素的时候) , 返回 队头元素
- 需要注意的是, 我们在把元素放入的时候, 只要后面的元素比前面的元素大, 就要把前面的元素移除掉, 因为我们要保证, 队头元素是当前窗口最大的元素
- 另外就是, 我们需要考虑队头过期的情况, 就是队头元素特别大, 后面的元素没有比它大的, 这样在后面的元素添加的时候它就不会被移除掉
- 但是当它过期, 也就是不在滑动窗口内的时候, 我们就要把它移除掉
如上图所示, 第一个元素 9 是第一个窗口的最大的元素, 但是当移动到第二个窗口的时候, 它就不是第二个窗口中最大的元素值了 , 我们要把它移除掉, 对应的代码是 :
// 队头过期 :
// 队头元素已经不在窗口内了
// i 是队尾元素, i - k 是最后一个元素
// 队头过期肯定是在第二个窗口才会出现过期的现象
while (!deque.isEmpty() && deque.getFirst() <= i - k) {
// 返回并删除队列头元素
deque.removeFirst();
}
代码流程
![image-20221012183946252](https://i-blog.csdnimg.cn/blog_migrate/2cc5945d531f381e0bd79845dcc93b14.png)
代码实现
package leetcode.editor.cn;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 滑动窗口最大值
*
* @author
* @date 2022-10-04 18:19:29
*/
@SuppressWarnings("all")
public class SlidingWindowMaximum {
public static void main(String[] args) {
//测试代码
Solution solution = new SlidingWindowMaximum().new Solution();
int[] nums = new int[]{1, 3, -1, -3, 5, 3, 6, 7};
int k = 3;
solution.maxSlidingWindow(nums, k);
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] ans = new int[nums.length - k + 1];
int index = 0;
// 双端队列, 存储下标
Deque<Integer> deque = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
// 队头过期 :
// 队头元素已经不在窗口内了
while (!deque.isEmpty() && deque.getFirst() <= i - k) {
// 返回并删除队列头元素
deque.removeFirst();
}
// 如果nums[i] > nums[deque.getLast()]
// 说明队列中 前面的元素就都没用了, 因为 nums[i] 要比前面的元素都大
// 我们就要移除前面的元素, 要保证队头元素是最大的!
while (!deque.isEmpty() && nums[deque.getLast()] <= nums[i]) {
// 移除队尾元素
deque.removeLast();
}
deque.addLast(i);
// 取队头更新元素
if (i >= k - 1) {
ans[index] = nums[deque.getFirst()];
index++;
}
}
return ans;
}
}
//leetcode submit region end(Prohibit modification and deletion)
}