第一章 栈和队列
1.7 生成窗口最大值数组
【题目】
一个整形数组 arr 和 一个大小为 w 的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。如果数组长度为 n,窗口大小为 w,则一共产生 n-w+1 个窗口的最大值。
请实现一个函数:
- 输入:整形数组 arr,窗口大小为 w。
- 输出:一个长度为 n-w+1 的数组 res,res[i] 表示每一个窗口状态下的最大值。
【难度】
尉 ★★☆☆
【题解】
本题最容易想到的是时间复杂度为 O(N×w) 的解法,但是并不符合题意。如果要达到时间复杂度 O(N) ,则需要借助双端队列实现窗口最大值的更新。首先生成双端队列 deque,其中存放数组 arr 中的下标。
- 假设遍历到 arr[i],deque 的入队规则为:
- 如果 deque 为空,直接把下标加入 deque 队尾;
- 如果 deque 不为空,取当前 deque 队尾存放的下标,假设为 j:
- 如果 arr[j] > arr[i],直接把下标 i 加入 deque 的队尾;
- 如果 arr[j] ≤ arr[i],把下标 j 从 deque 队尾弹出。
- 假设遍历到 arr[i],deque 的出队规则为:如果 deque 队头的下标小于等于 i-w,则当前 deque 队头的下标已过期,即当前下标位于窗口外。此时弹出当前队头的下标即可。
- 根据 deque 的入队规则和出队规则,deque 拥有维护窗口大小为 w 的子数组的最大值更新的结构。
上述过程中,每个下标最多进 deque 一次,出 deque 一次,所以在遍历过程中双端队列的操作的时间复杂度为 O(N)。
【实现】
- MaxWindow.java
import java.util.Deque;
import java.util.LinkedList;
public class MaxWindow {
private int[] arr;
private int window;
private int[] res;
public MaxWindow() {
}
public MaxWindow(int[] arr, int window) {
this.arr = arr;
this.window = window;
}
public void setArrAndWindow(int[] arr, int window) {
this.arr = arr;
this.window = window;
calculate();
}
public int[] getRes() {
if (this.res == null) {
calculate();
}
return this.res;
}
private void calculate() {
/**
* 窗口大小小于 1 or 数组为空 or 数组长度小于窗口长度 => 无结果
*/
if (this.window < 1 || this.arr == null || this.arr.length < this.window) {
return;
}
/**
* 辅助双端队列
*/
Deque<Integer> deque = new LinkedList<>();
/**
* 窗口最大值数组
*/
this.res = new int[this.arr.length - this.window + 1];
for (int i = 0, index = 0; i < this.arr.length; ++i) {
/**
* 队列不为空 and 队尾不大于下一个 => 队尾出队
* util 队列为空 or 队尾大于下一个
*/
while (!deque.isEmpty() && this.arr[deque.peekLast()] <= this.arr[i]) {
deque.pollLast();
}
/**
* 下一个入队
*/
deque.addLast(i);
/**
* 队首处于窗口左边界外 => 队首出队
*/
if (deque.peekFirst() == i - this.window) {
deque.pollFirst();
}
/**
* 窗口右边界达到窗口大小 => 当前窗口最大值即为队首
*/
if (i >= this.window - 1) {
this.res[index++] = this.arr[deque.peekFirst()];
}
}
}
}
- MaxWindowTest.java
public class MaxWindowTest {
public static void main(String[] args) {
int[] arr = {4, 3, 5, 4, 3, 3, 6, 7};
int window = 3;
MaxWindow maxWindow = new MaxWindow(arr, window);
maxWindow.getRes();
}
}