import java.util.*;
public class Solution {
/**
* 本题的解题思路很值得学习
* 简单的方法就是每个滑动窗口我们都遍历一遍来寻找最大值
* 可是这样子的时间复杂度就为 O(len*size) 时间复杂度太高了,因此我们就只能能改进方法
* 改进后的方法:因为我们只需要知道每个滑动窗口中的最大值,因此同一个滑动窗口中的比最大值
* 小的值均没有意义,即我们不需要他们,因此我们这里通过一个双端队列(看答案学的,这里的
* 思想很重要)来维护,双端队列中存的是元素对应在 num 数组中的下标(这个思想也很重要,
* 存下标正好方便我们判断当前元素是否还在滑动窗口内,若存入值,就不好判断),具体的维护
* 方法就是每次我们滑动窗口变化的时候,先判断队列头部元素是否在当前窗口内,不在就 poll
* 掉,之后我们在新元素入队列时要"挤"掉前面小于它的数(这些数就属于小于最大值的数,我们不
* 需要他们),这样子队列头就一直是当前滑动窗口中的最大值.之后每组就遵守相同的规则,同时将
* 队列头部元素入 res 即可.
* 改进后的时间复杂度:O(len)
* @param num
* @param size
* @return
*/
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> res = new ArrayList<>();
//排除不合法的条件
if (size <= 0 || size > num.length) {
return res;
}
//辅助双端队列 (其中存入的是下标)
Deque<Integer> deque = new ArrayDeque<>();
//先将前 size 个元素入双端队列,且每个元素进入的时候都"挤"掉前面小于它的数
for (int i = 0; i < size; i++) {
while (!deque.isEmpty() && num[deque.peekLast()] < num[i]) {
deque.pollLast();
}
deque.offerLast(i);
}
//先将这一组的最大值放入 res 中
res.add(num[deque.peekFirst()]);
//处理后续
for (int i = size; i < num.length; i++) {
//先判断双端队列中头部元素是否当前滑动窗口内,不在就弹出
if (deque.peekFirst() < i - size + 1) {
deque.pollFirst();
}
//将当前窗口要加入的值入双端队列,规则与之前相同
while (!deque.isEmpty() && num[deque.peekLast()] < num[i]) {
deque.pollLast();
}
deque.offerLast(i);
//将当前滑动窗口的最大值加入 res 中
res.add(num[deque.peekFirst()]);
}
return res;
}
}