目录
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
测试用例
- 功能测试(输入数组的数字大小无序;输入数组额数字单调递增;输入数组的数字单调递减)
- 边界值测试(滑动窗口的大小为0、1、等于输入数组的长度、大于输入数组的长度)
- 特殊输入测试(输入数组为空)
题目考点
- 考察应聘者分析问题的能力。
解题思路
利用一个双端队列(存数组下标),并永远保持队首为最大值。
在过程中需要保证两点:
- 队列中后面的元素可以比前面小,因为当前面的元素在滑出窗口的时候,后面小的元素可能是最大值。
- 队列中的数组首尾存的下标的差值不能大于等于活动窗口大小,因为滑动窗口在那。
详细见表格,方便理解。
举例{2,3,4,2,6,2,5,1}, size = 3
1、不完整窗口 + 完整窗口 + 最后一个窗口;
2、当前数字比队列中对应数字大,这样他们就不可能为当前窗口最大值,则将他们逐个弹出,并且将可能为最大值的当前数字下标存入双队列;
3、队列对应数字必须在当前窗口内,保证条件:当前数字下标 - 队列队首下标 < size , 反之则不满足,删除队首;
步骤 | 当前数字num[i] | 当前数字下标i | 滑动窗口 | 双端队列index 存的是前面数字 | 当前滑动窗口最大值 |
1 | 2 | 0 | 2 | 0(2) | NA |
2 | 3 | 1 | 2,3 | 1(3) | NA |
3 | 4 | 2 | 2,3,4 | 2(4) | 4 |
4 | 2 | 3 | 3,4,2 | 2(4),3(2) | 4 |
5 | 6 | 4 | 4,2,6 | 4(6) | 6 |
6 | 2 | 5 | 2,6,2 | 4(6),5(2) | 6 |
7 | 5 | 6 | 6,2,5 | 4(6),6(5) | 6 |
8 | 1 | 7 | 2,5,1 | 6(5), 7(1) | 5 |
参考解题
import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
// 理解队列,双端队列
public ArrayList<Integer> maxInWindows(int [] num, int size){
ArrayList<Integer> result = new ArrayList<Integer>();
// 异常
if(num.length < size || size < 1 ){
return result;
}
// 双端队列,保存的是数组中元素的下标
LinkedList<Integer> index = new LinkedList<>();
// 第一个滑动窗口,元素不完整的窗口
for(int i = 0; i < size; ++i){
while(!index.isEmpty() && num[i] >= num[index.getLast()]){
// 存入一个数字的下标前,检查队列数字是否小于待存入数字
// 是则依次从队尾弹出这些队列数字
index.pollLast();
}
// 存入数字下标
index.offer(i);
}
// 元素完整的窗口
for(int i = size; i < num.length; ++i){
// 队首为滑动窗口最大值
result.add(num[index.getFirst()]);
while(!index.isEmpty() && num[i] >= num[index.getLast()]){
index.pollLast();
}
// 如果队首不在滑动窗口内,即当前数字减去队列中队首元素的差大于等于size,则移除队首
if(!index.isEmpty() && i - index.getFirst() >= size) {
index.pollFirst();
}
index.offer(i);
}
// 最后一个滑动窗口
result.add(num[index.pollFirst()]);
return result;
}
}