题目描述:html
给定一个数组和滑动窗口的大小,找出全部滑动窗口里数值的最大值。例如,若是输入数组{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]}。java
解题思路:编程
若是不考虑时间开销,使用蛮力法,本题并不难解决,依次遍历全部的滑动窗口,扫描每一个窗口中的全部数字并找出其中的最大值,这都很容易实现,可是若是滑动窗口的大小为k,那么须要O(k)的时间找最大值,对于长度为n大的数组,总的时间复杂度为O(nk)。数组
而后咱们考虑进一步优化,一个滑动窗口实际上能够当作一个队列。当窗口滑动时,处于窗口第一个位置的数字被删除,同时在窗口的末尾又增长了一个新的数字。这符合队列“先进先出”的特性。函数
在第20题:包含min函数的栈,咱们使用两个栈实现了一个求最小值的栈,在O(1)时间内能够获得最小值,这里咱们能够改成求最大值,一样能够在O(1)时间内获得最大值,而这里的数据用队列保存,咱们能够用两个栈实现队列,这就是第5题:用两个栈实现队列。这样,实际上综合这两题咱们能够解决本题,总的时间复杂度也能够降到O(n)。优化
这里咱们换用另一种方法:使用双端队列。咱们不把全部的值都加入滑动窗口,而是只把有可能成为最大值的数加入滑动窗口。这就须要一个两边均可以操做的双向队列。code
咱们以数组{2,3,4,2,6,2,5,1}为例,滑动窗口大小为3,先把第一个数字2加入队列,第二个数字是3,比2大,因此2不多是最大值,因此把2删除,3存入队列。第三个数是4,比3大,一样删3存4,此时滑动窗口以遍历三个数字,最大值4在队列的头部。htm
第4个数字是2,比队列中的数字4小,当4滑出去之后,2仍是有可能成为最大值的,所以将2加入队列尾部,此时最大值4仍在队列的头部。blog
第五个数字是6,队列的数字4和2都比它小,因此删掉4和2,将6存入队列尾部,此时最大值6在队列头部。队列
第六个数字是2,此时队列中的数6比2大,因此2之后还有多是最大值,因此加入队列尾部,此时最大值6在仍然队列头部。
······
依次进行,这样每次的最大值都在队列头部。
还有一点须要注意的是:若是后面的数字都比前面的小,那么加入到队列中的数可能超过窗口大小,这时须要判断滑动窗口是否包含队头的这个元素,为了进行这个检查,咱们能够在队列中存储数字在数组中的下标,而不是数值,当一个数字的下标和当前出来的数字下标之差大于等于滑动窗口的大小时,这个元素就应该从队列中删除。
举例:
<div align=center>
</div>
编程实现(Java):
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; } }