滑动窗口-Java
-
生成窗口的最大值或者最小值数组,时间复杂度:O(N)。
-
普通解法的时间复杂度为O(N * win),也就是每次对一个窗口遍历其中的 win 个数,选出最大值,最优解可以做到 O(N)。
-
【分析】:**准备一个双端队列,双端队列存放着数组中的下标值。**假设当前为 arr[i],则放入规则如下:
-
left 和 right 指针都只会向右移动,不会回退。
-
right 右滑,窗口加数:
- 1)如果 queue 为空,直接把下标 i 放入 queue 中;
- 2)如果 queue 不为空,取出当前 queue 队尾存放的下标 j。如果 arr[j] > arr[i],则直接把 i 放入队尾;
- 3)如果 arr[j] <= arr[i],则一直从 queue 的队尾弹出下标,直到某个下标在 queue 中的对应值大于 arr[i],然后把 i 放入队尾 【为什么可以弹出,因为我永远比你晚过期,我又比你大或者和你一样大,有我在,你永远不可能最大,所以你可以滚了】
-
left 右滑,窗口减数:
- 1)看弹出的 left 是否与队列头相等,如果相等,说明这个队列头已经不在窗口内了,所以弹出 queue 当前的队首元素 。
-
双端队列的队头就是当前窗口最大值的下标。
public class Window {
public int[] arr;
private int left;
private int right;
private final LinkedList<Integer> queue;
public Window(int[] arr){
this.arr = arr;
left = 0;
right = 0;
queue = new LinkedList<>();
}
public Integer getMax(){
if (queue.isEmpty()){
return null;
}
return arr[queue.peekFirst()];
}
// 往滑动窗口加数时对双端队列的操作
public void addNumToRight(){
if (right == arr.length){
// right已经到达最右了,滑动窗口无法再增加数了
return;
}
while (!queue.isEmpty() && arr[queue.peekLast()] <= arr[right]){
queue.pollLast();
}
queue.add(right);
right++;
}
// 移除双端队列最左边的值
public void removeNumFromLeft(