给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
之前在剑指offer做这个题的时候,使用优先队列构先建堆,每次清空,时间和空间复杂度都较高。
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> result=new ArrayList<Integer>();
if(num==null||size<=0||num.length<size)
return result;
PriorityQueue<Integer> queue=new PriorityQueue<Integer>(size,(x,y)->y-x);
//lamda表达式实现降序排列
int count=0;
for(int i=0;i<num.length-size+1;i++)
{
while(count<size)
{
queue.offer(num[i+count]);//从第i个开始的size窗口
count++;
}
result.add(queue.peek());
count=0;//计数器清空
queue.clear();//最大堆清空
}
return result;
}
}
利用单调栈的性质,维持一个双端队列,队首存放最大的元素。添加元素时候,按照单调栈的规则添加到队列后面:当队列尾端一直小于当前元素时候,出栈,直到当前元素下雨队列尾部的元素。然后在滑动窗口向右滑动时候,需要考虑当前数组的元素如果刚好在双端队列的队首,表示已经遍历过下,需要移除队列。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n=nums.length;
if(n==0||k==0)
return new int[0];
Deque<Integer>deque=new ArrayDeque<>();//单调栈,队首存放最大元素
int[]ans=new int[n-k+1];
for(int i=0;i<k;i++){
while(!deque.isEmpty()&&deque.peekLast()<nums[i])//没有形成队列的时候
deque.removeLast();
deque.addLast(nums[i]);
}
ans[0]=deque.peekFirst();
for(int i=k;i<n;i++){
if(deque.peekFirst()==nums[i-k])//如果nums当前遍历的元素在deque的队首,就需要从队列中移除
deque.removeFirst();
while(!deque.isEmpty()&&deque.peekLast()<nums[i])
deque.removeLast();
deque.addLast(nums[i]);
ans[i-k+1]=deque.peekFirst();//每次滑动窗口的最大值都是在队首
}
return ans;
}
}