题目
给定一个数组 nums 和滑动窗口的大小 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 ||||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
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数的大小。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof
一、标记下标方法
定义
oldMax,oldMaxIndex,分别用来存储老的最大值和老的最大值下标,
- 通过遍历去寻找新的max,当max与oldMax不用,则令oldMax=max。
- 通过窗口右边界 r-oldMaxIndex是否 < k 来判断旧的最大值是否在窗口内:
- 当oldMax在窗口内,那么只需要执行一次判断(oldmax,nums[r])的大小,复杂度为O(1);
- 当oldMax不在窗口内。就遍历一次窗口内的值找出最大值并重新赋值给oldMax。
关键思想就上面这几条。
通过定义index自增为res[]数组赋值
代码写的比较直白,耐心看很好懂,如下:
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums==null||nums.length==0) return nums;
int[] res = new int[nums.length-k+1];int index =0;
int oldMax=Integer.MIN_VALUE;
int oldMaxIndex = 0;
int max = 0;
for(int i=0;i<k;i++){
max = Math.max(oldMax,nums[i]);
if(max!=oldMax){
oldMax = max;
oldMaxIndex = i;
}
}
res[index++]=max;
int r = k;
while(r<nums.length){
//oldMaxIndex在滑动窗口中,那么只需要比较(max,nums[r]);
if(r-oldMaxIndex<k){
max = Math.max(oldMax,nums[r]);
if(max!=oldMax){
oldMax=max;
oldMaxIndex=r;
}
}else{
oldMax=Integer.MIN_VALUE;
for(int i = r-k+1;i<=r;i++){
max = Math.max(oldMax,nums[i]);
if(max!=oldMax){
oldMax=max;
oldMaxIndex=i;
}
}
}
res[index++]=max;
r++;
}
return res;
}
二、单调队列
这题要求用队列完成,我也莫得办法,也不敢确定面试时面试官喜欢第一种方法还是这种,学习路途漫漫啊~
画了一张滑动窗口的图,仔细看图:
这标题既然写着队列,再联想到这图,滑动窗口的下一步就是5进来,3出来,这像什么?
嗯哼~,先进先出 -> 队列麻
那么我们再看一个队列:
[1,3,2,5]
在这个队列中取最大值,当5插入进来后,根据先进先出的特性,无论前面的[1,3,2]哪个出队都不会影响5是队列中的最大值,因为[1,3,2]总是在5之前出队。
根据这个思路,我们可以定义一个单调递减队列
当插入一个数时,有两种情况如队列:[4,3,3]
- 当待插入的数为5时,依次对比队列中尾部的数,当尾部的数(例如 3 )比 5 小,那么将 3 移除。
- 当带插入的数为2时,依次对比队列中尾部的数,当尾部的数比 2 大,那么将2插入到队列尾部
–
- 上面的过程保证了只要在元素 value 被插入之前队列递减,那么在 value 被插入之后队列依然递减。
- 而队列的初始状态(空队列)符合单调递减的定义。
- 由数学归纳法可知队列将会始终保持单调递减。
另:队列仅包含窗口内的元素,当窗口最左边元素因窗口右移而被移出,又当这个数正好是队列中的值时该怎么办呢? 咱们可以用Deque.peekFirst()==(被移出的数),是则remove,否则不用管。
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums==null||nums.length==0) return nums;
//因为队列双端都得操做,所以用的双端队列
Deque<Integer> eq = new LinkedList<>();
//储存最大值
int[] res = new int[nums.length-k+1];
//i与res下标挂钩,初始为1-k,i++,当i为0时,刚好滑动窗口的大小为k
for(int j=0,i=1-k;j<nums.length;i++,j++){
//窗口右移,删除相应的元素
if(i>0&&eq.peekFirst()==nums[i-1]){
eq.removeFirst();
}
while(!eq.isEmpty()&&eq.peekLast()<nums[j])
eq.pollLast();
eq.offerLast(nums[j]);
if(i>=0){
res[i] = eq.peekFirst();
}
}
return res;
}
队列解法参考力扣用户Krahets解析
https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/mian-shi-ti-59-i-hua-dong-chuang-kou-de-zui-da-1-6/
侵删