239. 滑动窗口最大值 - 力扣(LeetCode)
分析:
1.滑动窗口是有元素先进先出的特性的所以可以考虑到队列
2.因为前面的元素是要被删除的,如果考虑常规做法,需要不断地重复计算已经计算过的值
知识点:单调队列=双端队列+单调性
为什么要保证单调?
我们现在要求得是窗口内的最大值,也就是说,如果进来了比当前最大值还要大的,那么当前值在后续将不再成为最大值,所以可以把它清除出去,但是如果进来的是比现在的小的,虽然当前不是最大值,但是当窗口划过当前值之后,他就有可能成为最大值了,所以要保留,这就是为什么求滑动窗口最大值的时候要保证递减;同理,求滑动窗口最小值的时候,要保留比当前值大的数,而不用保留比当前值小的数,所以要保证递增
单调性如何确定?(以严格递减为例)
在队列非空,并且排队的元素比现在队列的最后一个元素大的时候
如果这个排队的元素进来,会导致严格递减不成立
所以把最后一个元素删除
但是这并不意味着倒数第二个,第三个元素就比排队的要大,所以在这一步要用到循环
这样才可以保证新元素进来的时候依然是是递减的,这个时候才让新元素进来
为什么要在队列中放序号而不是数值?
因为是窗口,要保证先进先出,如果直接放数值,那么就没有窗口的顺序了-----要保证数据是在窗口内的
新元素进来可能会导致窗口溢出,所以要判断需不需要把最前面的元素删除
在本题中,还要顾及到记录该窗口的最大值
java
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
public class test {
static void main() {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int k = 1;
System.out.println(Arrays.toString(maxSlidingWindow(nums, k)));
}
public static int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> queue = new ArrayDeque<>();
int[] arr = new int[nums.length - k + 1];
//遍历
for (int i = 0; i < nums.length; i++) {
//先加新数
//判queue空不空
while (!queue.isEmpty() && nums[queue.getLast()] < nums[i]) {
//如果新来的数更大
//那么前面的都不再是我后面会用到的最大值
//把比新来的小的都清除
queue.removeLast();
}
//没满的则要判断是否进队
//如果新来的数更小
//那就让他进来,因为可能是后面的数里面的最大值
queue.addLast(i);
//判断该不该清除前面的(size够不够)
int left = i - k + 1;
if (left > queue.getFirst()) {
//要把左边的移出去
queue.removeFirst();
}
//记录滑动窗口的最大值
if (left >= 0) arr[left] = nums[queue.getFirst()];
}
return arr;
}
}
1438. 绝对差不超过限制的最长连续子数组
选择滑动窗口的原因:子数组越长,最大值越大,最小值越小,越不能满足本题的要求,反之,子数组越短,越能满足本题的要求--------满足这样的性质的,可以用滑动窗口
由于要计算的是最大值和最小值之间的差异,所以要记录最大值和最小值两个数值,要定义两个队列,一个用来存放最大值,一个放最小值
但是最大值越大,最小值越小,越容易不满足题目要求-------所以最大值要缩小,最小值要变大--------所以最大值队列要递减,最小值队列要递增
如果最大值和最小值之间的差距过大了,那么就要踢出去他们其中的一个----谁先进来的谁先出去----所以要有一个数值记录窗口的开始在哪里,如果当前超出限制,那么窗口就要滑动,也就是开始值要加一
java
import java.util.ArrayDeque;
import java.util.Deque;
public class test {
static void main() {
int[] nums = {10, 1, 2, 4, 7, 2};
int k = 5;
System.out.println(maxSlidingWindow(nums, k));
}
public static int maxSlidingWindow(int[] nums, int limit) {
Deque<Integer> maxqueue = new ArrayDeque<>();
Deque<Integer> minqueue = new ArrayDeque<>();
int left = 0;
int maxnum = 0;
for (int i = 0; i < nums.length; i++) {
//让最大队列递减,让最小队列递增
while (!maxqueue.isEmpty() && nums[maxqueue.getLast()] < nums[i]) {
maxqueue.removeLast();
}
maxqueue.addLast(i);
while (!minqueue.isEmpty() && nums[minqueue.getLast()] > nums[i]) {
minqueue.removeLast();
}
minqueue.addLast(i);
//做判断,如果在大于限制,要右滑窗口,把最前面的窗口移出去
while (nums[maxqueue.getFirst()] - nums[minqueue.getFirst()] > limit) {
left++;
if (maxqueue.getFirst() < left) {
maxqueue.removeFirst();
}
if (minqueue.getFirst() < left) {
minqueue.removeFirst();
}
}
maxnum = Math.max(maxnum, i - left + 1);
}
return maxnum;
}
}
学习思路来自灵茶山艾府 - 力扣(LeetCode)