滑动窗口
- 可以理解为一个可以容纳n个元素的窗口,通过每次记录窗口的状态,找到符合条件的窗口或者说在窗口中得到符合条件的元素。
- 通过滑动窗口可以减少时间复杂度
【题目】有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次 向右边滑一个位置。
例如, 数组为[4,3,5,4,3,3,6,7], 窗口大小为3时:
窗口滑动过程 | 窗口中的最大值 |
---|---|
[ 4 3 5 ] 4 3 3 6 7 | 5 |
4 [3 5 4] 3 3 6 7 | 5 |
4 3 [ 5 4 3] 3 6 7 | 5 |
4 3 5 [ 4 3 3 ] 6 7 | 4 |
4 3 5 4 [3 3 6 ] 7 | 6 |
4 3 5 4 3 [ 3 6 7] | 7 |
如果数组长度为n, 窗口大小为w, 则一共产生n-w+1个窗口的最大值。
请实现一个函数。 输入:整型数组arr, 窗口大小为w。
输出:一个长度为n-w+1的数组res, res[i]表示每一种窗口状态下的 以本题为例, 结果应该返回{5,5,5,4,6,7}。
【代码实现】
import java.util.Arrays;
import java.util.LinkedList;
public class SildingWindow {
public static void main(String[] args) {
int[] arr={4,3,5,4,3,3,6,7};
int[] res = slidingWindow(arr, 3);
System.out.println(Arrays.toString(res));
}
/*如果数组长度为n, 窗口大小为w, 则一共产生n-w+1个窗口的最大值。
请实现一个函数。 输入:整型数组arr, 窗口大小为w。
输出:一个长度为n-w+1的数组res, res[i]表示每一种窗口状态下的 以本题为例, 结果应该
返回{5,5,5,4,6,7}。*/
public static int[] slidingWindow(int[] arr, int w) {
//1.定义一个双端队列,存放的是arr数组的下标
LinkedList<Integer> deque = new LinkedList<>();
//2.定义一个数组用来保存窗口最大值
int[] res = new int[arr.length - w +1];
int index=0; //辅助变量给res使用
for (int i = 0; i < arr.length; i++) { //3.从左到右移动窗口
while (!deque.isEmpty() && arr[deque.peekLast()] <= arr[i]) { // 4.判断添加的时候是否需要将deque中last元素弹出
//当deque中最后一个值对应的arr的值小于或者等于ar[i]的时候,要将deque最后的值弹出直到arr[last]的值大于arr[i]或者deque为空
deque.pollLast();
}
deque.addLast(i); //将当前位置的索引加入到队列中
if(deque.peekFirst()==i-w){
deque.pollFirst();
}
if (i >=w - 1) { //由于i要至少到大w-1位置这个时候窗口才是满的 刚开始窗口中的数的个数为1,2...w
res[index++]=arr[deque.peekFirst()]; //5.将窗口最大值加入数组
}
}
return res;
}
}
【总结】
-
每个位置的数最多进窗口一次出窗口一次,弹出过的数不会找回
-
如果滑过的数字为n,双端队列更新的总代价时间复杂度O(N)
-
双端队列滑过n次,双端队列更新的平均代价时间复杂度O(1)