打卡第十天~
题目描述
- 久违的滑动窗口题!
思路 && 代码
1. 暴力法 O( n 2 n^2 n2) && O(1)
- 老规矩,先来个暴力法熟悉一下题目~
- 出乎意料没有超时…不过我们还是需要寻找更优的做法!
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0) {
return new int[]{};
}
int[] res = new int[nums.length - k + 1];
// 暴力
for(int i = 0; i < res.length; i++) {
int max = nums[i];
for(int j = 0; j < k; j++) {
max = Math.max(max, nums[i + j]);
}
res[i] = max;
}
return res;
}
}
2. 单调队列辅助 O(n) && O(n)
- 空间换时间!
- 利用一个单调递减的队列,始终把当前滑动窗口最大值置于队头
- 更新判断一:当前队头是否已经脱离滑动窗口。(我觉得这块具体代码不太好想到)
- 更新判断二:加入当前数组元素,是否会改变队列递减特性
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0) {
return new int[]{};
}
// 队列头:记录滑动窗口最大值
Deque<Integer> deque = new LinkedList<>();
int[] res = new int[nums.length - k + 1];
for(int i = 0, windowTail = 1 - k; i < nums.length; i++, windowTail++) {
// 更新一:去掉前一个窗口尾值
if(windowTail > 0 && deque.peekFirst() == nums[windowTail - 1]) {
deque.removeFirst();
}
// 更新二:保持递减
while(!deque.isEmpty() && deque.peekLast() < nums[i]) {
deque.removeLast();
}
deque.addLast(nums[i]);
// 记录窗口最大值
if(windowTail >= 0) {
// 窗户尾值,同时也是 res 的对应下标
res[windowTail] = deque.peekFirst();
}
}
return res;
}
}
/*
1. 采用单调队列
2. 主要是 队列头 的处理
3. 通过维护 windowTail 来维护队列(重要)
*/
二刷
- 单调队列,找最大值,则需要单调减(队头为最大)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0) {
return new int[]{};
}
int[] ans = new int[nums.length - k + 1];
Deque<Integer> queue = new ArrayDeque<>();
for(int i = 0, windowTail = 1 - k; i < nums.length; i++, windowTail++) {
// step1: 去掉上一个窗口尾值
if(windowTail > 0 && queue.element() == nums[windowTail - 1]) {
queue.poll();
}
// step2: 保持递减(注意,没有按照正常的队列顺序,是从后面开始处理的
while(!queue.isEmpty() && nums[i] > queue.getLast()) {
queue.removeLast();
}
queue.offer(nums[i]);
if(windowTail >= 0) {
ans[windowTail] = queue.element();
}
}
return ans;
}
}