Leetcode.239. 滑动窗口最大值

编程入门 行业动态 更新时间:2024-10-25 22:33:44

Leetcode.239. 滑动窗口<a href=https://www.elefans.com/category/jswz/34/1768932.html style=最大值"/>

Leetcode.239. 滑动窗口最大值

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       31 [3  -1  -3] 5  3  6  7       31  3 [-1  -3  5] 3  6  7       51  3  -1 [-3  5  3] 6  7       51  3  -1  -3 [5  3  6] 7       61  3  -1  -3  5 [3  6  7]      7
示例 2:输入:nums = [1], k = 1
输出:[1]
示例 3:输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:输入:nums = [9,11], k = 2
输出:[11]
示例 5:输入:nums = [4,-2], k = 2
输出:[4]提示:1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length

题解:

方法一:暴力

  • 我们直接维护一个滑动窗口,接着按照要求进行poppush的操作即可,但是由于每次都要求一次滑窗里的max,所以此法很耗费时间。
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {List list = new ArrayList();int[] res = new int[nums.length];int index = 0;for(int i=0;i<nums.length;i++){if(list.size()<k){list.add(nums[i]);}else{res[index++] = cal(list);list.remove((int)0);list.add(nums[i]);}}res[index++] = cal(list);int[] temp = new int[index];for(int i=0;i<index;i++){temp[i] = res[i];}return temp;}public int cal(List list){int res = -20000;for(int i=0;i<list.size();i++){res = Math.max(res , (int)(list.get((int)i)));}return res;}
}

方法二:优化暴力

  • 在方法一的基础上,增加存储滑窗max值的设计,即在滑窗移动后求新的max时不是直接计算新的max,而是判断一下原先的max是否被移出滑窗,如果没有则继续用这个max,接着再判断一下要加进来的元素和原max之间的大小关系,如果要进滑窗的元素直接大于原max,则原max直接禅位即可。其实和下面法三有异曲同工之妙,在于维护滑窗的同时,维护了一个仅存储滑窗max值及其所处滑窗位置的res数组
  • 需要注意的是在cal函数中之所以要传入flag这个布尔类型数组,是因为值传递的话修改的只是形参,没有真正的修改值,所以这里使用数组进而使其变成引用传递,类似于c++中使用指针。
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {List list = new ArrayList();int[] res = new int[nums.length];int index = 0;int rep = 0;int[] ini = new int[2];boolean[] flag = new boolean[1];flag[0] = false;for(int i=0;i<nums.length;i++){if(list.size()<k){list.add(nums[i]);}else{ini = cal(list,flag,ini);res[index++] = ini[0];if(ini[1]==0){flag[0] = false;}if(nums[i]>ini[0]){ini[0] = nums[i];ini[1] = k-1;}list.remove((int)0);ini[1]--;list.add(nums[i]);}}ini = cal(list,flag,ini);res[index++] = ini[0];int[] temp = new int[index];for(int i=0;i<index;i++){temp[i] = res[i];}return temp;}public int[] cal(List list,boolean[] flag,int[] res){if(flag[0]){return res;}res[0] = -20000;for(int i=0;i<list.size();i++){int temp = res[0];res[0] = Math.max(res[0] , (int)(list.get((int)i)));if(res[0]!=temp){res[1] = i;}}flag[0] = !flag[0];return res;}
}

方法三:单调队列

我们设计一个单调队列类作为我们维护的滑窗,注意的是其只是维护一个单调递减的数列,这样的话我们在取队列里的最大值时直接拿头部元素即可。

  • 另外这个队列在添加滑窗尾部元素时不只是简单的添加元素,而是先进行判断,判断要加入的元素是否满足原队列的递减关系,如果满足直接添加即可;如果不满足,则判断一下其和队尾元素的关系,如果其大于队尾元素则将队尾元素干掉,自己加入进去,接着继续和队尾进行判断即可。
  • 此队列在删除滑窗头部元素时,也不是直接删除队头元素,因为此队列的顺序不是严格按照滑窗内元素的顺序来的,因此我们不可直接删除队头,而是判断一下要删除的元素是否是队头元素,是则真正的删除,不是则无需删除。
  • 通过设计这样一个单调队列,我们发现问题其实就迎刃而解,并且由于我们每次移动后由队列直接告诉我们最大值,我们无需自己去通过遍历滑窗来得到最大值,因此此法不会超时。

或许你会疑问此单调队列的poppush的设计原理:

  • 为什么push的时候当遇到要添加的元素大于队尾元素时,要删除队尾元素把自己加进去?

因为之所以维护一个单调递减队列,就是为了保存“某个滑窗的可能最大值”,首先队头元素一定是此时滑窗的最大值,接着如果滑窗移动一次后,队头元素可能还是此滑窗的最大值,但也有可能不是,即其刚好是被删除的情况。假如其不是,那么新队列的队头元素就是此滑窗的最大值。

而这个新队头的来源有两个,即要么为原先的队列二号位,要么是新加入滑窗的元素。即使新加入的元素很小,他也是有可能成为“某个滑窗的可能最大值”,因此不能删除。而当新加入的元素处于单调队列的区间内,之所以要实行干掉操作,是因为单调队列的长度是小于等于滑窗的长度,假设其不干掉小于他的元素,当滑窗移动到他应为滑窗最大值(在后面没别的元素干他的情况下)时,我们会发现此时的他不在队头,而且我们发现保留的前面的那些元素并没起到任何作用,因此前面那些元素我们需要干掉,来保证形成一个纯净的有用的单调队列

这里我们可以将单调队列理解为这么一个过程:

  • 单调队列可以理解为在修仙界的一个宗门,宗门的创始人必定是冷血无情,却又实力雄厚的,因此成为了老大,后来他变的仁慈起来,广收弟子。当弟子们十分强大时,又会冷血无情的将比自己弱的人全部干掉,包括老大,干掉之后又会恢复仁慈的性格,收自己的弟子,自己成为老大。当然老大也会自己老死,那么将由老二接管。单调队列便是这样一个如此循环往复的过程。
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {int[] res = new int[nums.length];int index = 0;Myquene myquene = new Myquene();for(int i=0;i<k;i++){myquene.push(nums[i]);}for(int i=k;i<nums.length;i++){res[index++] = myquene.front();myquene.push(nums[i]);myquene.pop(nums[i-k]);   }res[index++] = myquene.front();int[] temp = new int[index];for(int i=0;i<index;i++){temp[i] = res[i];}return temp;}}class Myquene{List<Integer> list = new ArrayList<>();int front(){return list.get((int)0);}void pop(int num){if(list.size()!=0 && num==front()){list.remove((int)0);}}void push(int num){while(list.size()!=0 && num>list.get(list.size()-1)){list.remove((int)(list.size()-1));}list.add(num);return;}
}

更多推荐

Leetcode.239. 滑动窗口最大值

本文发布于:2024-02-19 17:36:39,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1765090.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:最大值   窗口   Leetcode

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!