题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
例如,如果输入数组{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]}。
窗口大于数组长度的时候,返回空。
示例1
输入:[2,3,4,2,6,2,5,1],3
返回值:[4,4,6,6,6,5]
题目分析
对于滑动窗口来说,使用双指针的基本模板如下:
while(right<length){
while(窗口的长度<size){
right++; // 增加窗口长度
// 处理窗口内的数据
}
left++; // 移动左边界指针
}此处窗口数据的处理为获取最大值,可以直接暴力法遍历窗口内的所有数据,取最大值;
也可以构建单调递减的队列,每次移动左右指针时,更新队列中的数据,每个区间的最大值即为队列的头部数据。
解题思路
方法1:暴力法遍历区间最大值
根据区间长度size,可以确定数组中的每个区间(左边界和有边界),然后遍历这个区间内的所有数据,可以获取窗口的最大值。
// 确定区间的左边界
for(int i=0;i<nums.length-size;i++){
int max;
// 从左边界到右边界内的数据获取最大值
for(int j=0;j<size;j++){
max = Math.max(max, nums[i+j]);
}
}方法2:使用单调双向队列
暴力法需要重复遍历很多数据,效率不高,可以使用单调递减队列来存储遍历的数据,每次更新区间时,只要获取单调队列的头部数据即可;
主要思路:
1.创建单调递减队列,遍历区间内的数据;
2.如果后面的数据大于队列队尾的数据(区间最小值),需要将队列前面的数据出队列,保证队列的单调性;
3.如果后面的数据小于队列队尾数据,则直接入队列;
4.最关键的在于移动左下标时,需要将队列中的相应数据出队列(看下标小于左下标的要移除队列头);
5.基于上述情况,需要记录数组的下标,则可以直接在队列中存储下标,排序时使用下标对应的数字,同时考虑到队列同时需要从头部删除数据和从尾部删除数据,使用双向队列;
代码实现
方法1:暴力法遍历区间最大值
public ArrayList<Integer> maxInWindows(int [] num, int size){
ArrayList<Integer> result = new ArrayList<Integer>();
if(size==0 || size>num.length) return result;
// 遍历数组的所有区间
for(int i=0;i<=num.length-size;i++){
int max = num[i];
// 获取区间中的最大值
for(int j=1;j<size;j++){
if(max<num[i+j]){
max = num[i+j];
}
}
result.add(max);
}
return result;
}时间复杂度:,n是数组长度,k是区间长度,暴力遍历次数 nk;
空间复杂度:,需要使用常量个数的变量来遍历数组和保存数据;
方法2:使用单调双向队列
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> res = new ArrayList<>();
if(num == null || num.length == 0 || size == 0 || num.length < size) return res;
int left = 0;
int right = left+1;
// 单调递减双向队列
Deque<Integer> deque = new LinkedList<Integer>();
deque.offerLast(0);
while(right < num.length){
while((right-left)<size){
while(!deque.isEmpty() && num[right]>num[deque.peekLast()]){
// 若是后面的数比前面的大,则不需要存储前面的数
deque.pollLast();
}
// 直到遇到比后面的数大的,将后面的数加入栈中
deque.offerLast(right);
right++;
}
res.add(num[deque.peekFirst()]);
left++;
// 移动左下标,同时更新队列头部
while(!deque.isEmpty() && deque.peekFirst() < left){
deque.pollFirst();
}
}
return res;
}时间复杂度:,指针right移动的距离为数组的长度-size,时间复杂度为n;
空间复杂度:,单调队列中最多需要使用k的空间来存储区间数据。

京公网安备 11010502036488号