文章目录
- 题目
- 解题思路
- 代码展示
题目
给定一个长度为 n 的数组 num
和滑动窗口的大小 size
,窗口从最左边滑动到最右边,每次向右边滑动一个位置,找出所有滑动窗口里数值的最大值。
窗口大于数组长度或窗口长度为0的时候,返回空
数据范围: 1 ≤ n ≤ 10000,0 ≤ size ≤ 10000,数组中每个元素的值满足∣val∣ ≤ 10000
要求:空间复杂度 O(n),时间复杂度 O(n)
例如:
该例子中,数组长度为 n(8),窗口大小为 size(5),一共产生了 n - size + 1(4)个窗口与的最大值
最后返回的列表结果是 {5,5,6,7}
解题思路
该题使用到的数据结构就是双端队列
,双端队列,顾名思义,就是一个队列,并且在该队列中元素可以从队尾添加删除获取,也可以从队头添加删除获取
双端队列中存储的是元素的下标
,并且规定下标在数组中对应的值在双端队列中从头到尾保持严格的单调递减,所以在窗口形成时队列头部的下标就是当前窗口的最大值
当前队列为空,因此直接将下标 0 从队尾添加到队列中
由于该队列保持严格降序,1 下标的值 3 没有比 0 下标的值 4 大,因此,直接从队尾添加到队列中
此时,2 下标的对应的值 5,比 1 下标对应的值 3 大,因此下标 1 从队尾弹出,下标 0 也是如此,遵守这样的规定,依次遍历
终于,R >= size - 1,表示窗口正式形成了,此时队首的下标对应的元素就是当前窗口的最大值,添加到返回列表当中
接下来滑动窗口就要向右开始滑动了,L 和 R 都要向右开始移动一格,下标 0 过期了,如果队首是下标 0,那就该出队列了,如果不是,那无事发生
虽然下标 4 和下标 5 处的元素同为 3,但是下标 5更加晚过期,可以保留更久,所以下标 4 就没有保留的价值了,从队尾弹出。队头的下标未过期,将其值添加到返回列表中
最后获取到列表 {5,5,6,7}
每个元素下标只会进队列一次,出队列一次,所以时间复杂度为 O(N),因为使用到了双端队列,空间复杂度为 O(N)
代码展示
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> list = new ArrayList<>();
if(size == 0 || size > num.length) {
return list;
}
Deque<Integer> deque = new LinkedList<>();
for(int i = 0;i < num.length;i ++) {
while(!deque.isEmpty() && num[deque.peekLast()] <= num[i]) {
deque.pollLast();//不满足严格递减,弹出
}
deque.addLast(i);//添加元素
//队列顶部如果是过去下标就出队列
if(deque.peekFirst() == i-size) {
deque.pollFirst();
}
//窗口正式形成
if(i >= size-1) {
list.add(num[deque.peekFirst()]);
}
}
return list;
}