例子:
长度为n的数组 长度为m的窗口,求数组中每个长度为m的窗口中的最大值
n=8 m=3
1 3 4 7 6 2 5 1
思想:
求窗口中的最大值时,我们需要一个双端队列来进行操作,维持一个降序排序的队列
这样每次取出的对头元素即为此时窗口的最大值。
源码:
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Scanner;
public class 单调队列实现滑动窗口 {
public static void main(String[] args) {
/*
* 长度为n的数组 长度为m的窗口,求数组中每个长度为m的窗口中的最大值
*
* n=8 m = 3
* 1 3 4 7 6 2 5 1
*/
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = 3;
int a[] = new int[n+1];
for (int i = 1; i < a.length; i++) {
a[i] = scanner.nextInt();
}
//同时对队头和队尾进行一个操作
//对队尾元素进行删除(队尾元素小于当前元素的时候)和添加(遍历到了该元素)
//对队头元素进行删除 已经不在窗口内了
Deque<Integer> qDeque = new ArrayDeque<Integer>();//记录的是数组的下标
for (int i = 1; i < a.length; i++) {
//保证队头元素一定是在窗口内的
while(!qDeque.isEmpty() && qDeque.peekFirst() < i - m + 1) qDeque.pollFirst();
//保证当前队尾元素一定大于当前元素的值
while(!qDeque.isEmpty() && a[qDeque.peekLast()] < a[i]) qDeque.pollLast();
qDeque.addLast(i);
//为什么需要i>=m i代表的是窗口最右边的下标,i<m的时候,此时这个窗口还没有完全进来,i-m+1 窗口最左边的下标
//i<m i-m+1 负数
//为什么不可以直接让i从m开始遍历呢?
//数组前2个元素既没有进行比较,它也没有办法进入队列
//
if(i >= m) {
System.out.println(a[qDeque.peekFirst()]);
}
}
}
}