题目描述
题目链接239. 滑动窗口最大值
题解
维护双端单调递减队列:
- 维护一个单调递减队列,队列头部元素,就是此时滑动窗口中的最大值,队列中只存放可能是最大值的数,
- 若新入队列的值x,大于队列中的部分值,依次弹出这些值,入队新值。因为,滑动窗口其实也可以看作一个先进先出的队列,x是最后一个入队的,只要有x在,那么比他小的数,就必不可能成为最大值。所以要把它们弹出去。
- 举个例子:2 4 3。 滑动窗口大小为2。
- 遍历到2, 2入队。
- 遍历到4, 发现4比2大,所以弹出2。因为4一定是比2后出滑动窗口,所以有4在,2就永远不可能成为最大值。
- 遍历到3,3比4小,3入队。 因为3比4小,并且3在后面,所以4先出滑动窗口,4出去了之后,3是有机会成为最大值的。
- 窗口左边界移动,若左边界就是队列之中的最大值,则弹出队列顶端元素。
详见代码注释
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
//双端队列
Deque<Integer> deque = new LinkedList<>();
//left和right为滑动窗口的左右边界,count用来给res赋值。
int left = 0, right = 0, count = 0;
//滑动窗口初始化,前k个,光移动右边界
for (right = 0; right < k; right++){
//若新入队的nums[right]大于队列中的部分值,则把这些值弹出。
while (!deque.isEmpty() && nums[right] > deque.peekLast()){
deque.pollLast();
}
deque.addLast(nums[right]);
}
//第一个滑动窗口的最大值给到res
res[count++] = deque.peekFirst();
//开始移动滑动窗口,左右边界同时向前,算移动完成。
while (right < nums.length){
//移动右边界,同样,如果队列中有比nums[right]小的数,都弹出队列,因为有nums[right]在,他们就永远不能成为最大值,就不用维护了。
while (!deque.isEmpty() && nums[right] > deque.peekLast()){
deque.pollLast();
}
deque.addLast(nums[right]);
right++;
//移动左边界,如果即将从滑动窗口出去的正是此时滑动窗口的最大值,要从队列中弹出。
if (nums[left] == deque.peekFirst()){
deque.pollFirst();
}
left++;
//此时滑动窗口已经移动完,队列头部元素就是此时滑动窗口中的最大值,赋值给res
res[count++] = deque.peekFirst();
}
return res;
}
}