题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
方法一:设置滑动窗口为k,可以扫描每个滑动窗口的所有数字并找出其中的最大值。如果滑动窗口为k,则需要O(k)时间才能找出滑动窗口的最大值。对于长度为n的输入数组,该方法的时间复杂度为O(kn)。
代码实现:
class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> list = new ArrayList<>();
if(num==null||size<=0)
return list;
int i = 0; //i为滑动窗口的开始
int j = size - 1; //i为滑动窗口的结束
while(j<=num.length-1){
list.add(maxValue(num,i,j)); //将每个滑动窗口的值加入列表中
i = i + 1; //向前滑动一位
j = j + 1; //向前滑动一位
}
return list;
}
public int maxValue(int[] a ,int i,int j){ //找出数组从a[i]到a[j]的最大值
int temp = Integer.MIN_VALUE;
for(int k=i;k<=j;k++){
if(temp<a[k])
temp = a[k];
}
return temp;
}
}
方法二(课本推荐的方法):
使用一个双向队列(所谓双向队列,是指队头和队尾都可以出元素)作为辅助。队列中存放数组的下标,主要步骤:
1)遍历数组,取出元素,如果此时队列为空,直接将元素加入队列
2)如果取出数组元素的下标和队列头元素的下标的间距超过了滑动窗口的大小,就将队列头元素弹出,这一步保证了队列中的元素的个数永远小于滑动窗口的大小。
3)如果取出的数组的元素大于队列中已有的元素,就弹出比数组元素小的元素
4)向队列中添加元素。
class Solution {
public ArrayList<Integer> maxInWindows(int[] num, int size) {
ArrayList<Integer> list = new ArrayList<>();
ArrayDeque<Integer> q = new ArrayDeque<>();
for(int i=0;i<num.length;i++){
if(q.isEmpty()){
q.add(i); //存入的是下标
}
if(i-q.peekFirst()>=size){ //超过滑动窗口大小了,从队头取出元素
q.pollFirst();
}
while(!q.isEmpty()&&num[q.peekLast()]<=num[i]){//如果加入的元素大于队列中已有的元素,已有元素就出队列
q.pollLast();
}
q.add(i); //每次遍历数组,都要加入一个元素
//System.out.println(q);
if(i>=size-1){ //从第三个数字开始
list.add(num[q.peekFirst()]); //从尾巴开始取出
}
}
return list;
}
}
说明:ArrayDeque的基本使用:
ArrayDeque<Integer> q = new ArrayDeque<>();
q.add(2); //加入队列队尾
q.add(3);
q.push(4); //加入队列队首
System.out.println(q);
System.out.println(q.peekFirst()); //取出队首元素
System.out.println(q.peekLast()); //取出队尾元素
输出:
[4, 2, 3]
4
3