剑指offer64
滑动窗口的最大值
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
窗口大于数组长度的时候,返回空。
示例一
输入
[2,3,4,2,6,2,5,1],3
输出
[4,4,6,6,6,5]
思路
滑动窗口应当是队列,但为了得到滑动窗口的最大值,队列序可以从两端删除元素,因此使用双端队列。
原则: 对新来的元素k
,将其与双端队列中的元素相比较
1)前面比k
小的,直接移出队列(因为不再可能成为后面滑动窗口的最大值了!),
2)前面比k
大的X
,比较两者下标,判断X
是否已不在窗口之内,不在了,直接移出队列
队列的第一个元素是滑动窗口中的最大值;
特别说明,我们在双端队列中保存的数字是传入的向量的下标!
代码
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> ret = new ArrayList<>();
if (num == null || num.length < size || size < 1) {
return ret; //处理特殊情况
}
LinkedList<Integer> indexDeque = new LinkedList<>();
//处理前size个数据,因为这个时候不需要输出最大值
for (int i = 0; i < size - 1; i++) {
//假如当前的元素比队列队尾的元素大,说明之前加入的这些元素不可能是最大值了。直接移除
while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
indexDeque.removeLast();//弹出比当前小的元素下标
}
indexDeque.addLast(i);//队尾压入当前下标
}
//处理size往后的元素,这时候需要输出滑动窗口的最大值
for (int i = size - 1; i < num.length; i++) {
while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
indexDeque.removeLast(); //从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
}
indexDeque.addLast(i);//将当前下标压入队尾,因为可能在未来是最大值
if (i - indexDeque.getFirst() + 1 > size) {//判断队头的下标是否超出size大小,如果超过,要删除队头元素
indexDeque.removeFirst(); //当当前窗口移出队首元素所在的位置,即队首元素下标对应的num不在窗口中,需要弹出
}
ret.add(num[indexDeque.getFirst()]);//最后将队首元素添加进结果数组
}
return ret;
}
}
/**
ArrayDeque不是线程安全的。
ArrayDeque不可以存取null元素,因为系统根据某个位置是否为null来判断元素的存在。
当作为栈使用时,性能比Stack好;当作为队列使用时,性能比LinkedList好。
*/
此外,可以使用双端队列 ArrayDeque 类实现:
import java.util.*;
/**
用一个双端队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次
1.判断当前最大值是否过期
2.新增加的值从队尾开始比较,把所有比他小的值丢掉
*/
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> res = new ArrayList<>();
if(size == 0) return res;
int begin;
ArrayDeque<Integer> q = new ArrayDeque<>();
for(int i = 0; i < num.length; i++){
begin = i - size + 1;
if(q.isEmpty())
q.add(i);
else if(begin > q.peekFirst())
q.pollFirst();
while((!q.isEmpty()) && num[q.peekLast()] <= num[i])
q.pollLast();
q.add(i);
if(begin >= 0)
res.add(num[q.peekFirst()]);
}
return res;
}
}
附:ArrayDeque类
参考 https://blog.csdn.net/skh2015java/article/details/74840513