滑动窗口的最大值
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
方法一:暴力法
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size){
ArrayList<Integer> list = new ArrayList<Integer> ();
if(size>num.length||size==0)
return list;
for(int i = 0;i<=num.length-size;i++){
int max = num[i];
for(int j = i+1;j<i+size;j++){
if(num[j]>max){
max = num[j];
}
}
list.add(max);
}
return list;
}
}
双端队列
参考:
https://www.cnblogs.com/gzshan/p/10904273.html
使用双端队列。我们不把所有的值都加入滑动窗口,而是只把有可能成为最大值的数加入滑动窗口。这就需要一个两边都可以操作的双向队列。
我们以数组{2,3,4,2,6,2,5,1}为例,滑动窗口大小为3,先把第一个数字2加入队列,第二个数字是3,比2大,所以2不可能是最大值,所以把2删除,3存入队列。第三个数是4,比3大,同样删3存4,此时滑动窗口以遍历三个数字,最大值4在队列的头部。
第4个数字是2,比队列中的数字4小,当4滑出去以后,2还是有可能成为最大值的,因此将2加入队列尾部,此时最大值4仍在队列的头部。
第五个数字是6,队列的数字4和2都比它小,所以删掉4和2,将6存入队列尾部,此时最大值6在队列头部。
第六个数字是2,此时队列中的数6比2大,所以2以后还有可能是最大值,所以加入队列尾部,此时最大值6在仍然队列头部。
······
依次进行,这样每次的最大值都在队列头部。
还有一点需要注意的是:如果后面的数字都比前面的小,那么加入到队列中的数可能超过窗口大小 ,这时需要判断滑动窗口是否包含队头的这个元素,为了进行这个检查,我们可以 在队列中存储数字在数组中的下标 ,而不是数值,当一个数字的下标和当前出来的数字下标之差大于等于滑动窗口的大小时,这个元素就应该从队列中删除。
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size){
/*
思路:用双端队列实现
*/
ArrayList<Integer> res=new ArrayList<>();
if(num==null || num.length<1 || size<=0 || size>num.length)
return res;
Deque<Integer> queue=new LinkedList<>();
for(int i=0;i<num.length;i++){
while(!queue.isEmpty() && queue.peek()<i-size+1) //超出范围的去掉
queue.poll();
//当前值大于之前的值,之前的不可能是最大值,可以删掉
while(!queue.isEmpty() && num[i]>=num[queue.getLast()])
queue.removeLast();
queue.add(i);
if(i>=size-1){ //此时开始是第一个滑动窗口
res.add(num[queue.peek()]);
}
}
return res;
}
}
其他滑动窗口类的题目:
求无重复字符的最长字串