题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
窗口大于数组长度的时候,返回空
思路
一个滑动窗口可以看成一个队列,当窗口滑动时,处于窗口的第一个数字被删除,同时在窗口的末尾添加一个新的数字,这符合队列“先进先出”。
思路1:暴力法。时间复杂度为O(nk)
思路2:增减维护一个优先队列。时间复杂度O(n logk)
思路3:通过单调Deque双端队列。Deque里头最大,尾最小。注意Deque里存的是index,不是值。 (时间复杂度O(n),每个下标最多进一次,出一次。)
一共有n-(size-1)个滑动窗口
注意要分清队列的头和尾,“头在左,尾在右”。因为,窗口向右移动,队列是尾入头出。
步骤:头尾尾头
- 1)头部出队
- 2)尾部清理(因为要删除尾部,所以要用双端队列)
- 3)尾部入队
- 4)返回头部(从左往右看)
☆☆☆☆解法
import java.util.ArrayList; import java.util.Deque; import java.util.LinkedList; public class Solution { public ArrayList<Integer> maxInWindows(int [] num, int size) { ArrayList<Integer> list = new ArrayList<>(); if (num == null || size < 1 || num.length < size) return list; // 双端队列,deque里存的是数组的index,不是数组的值 Deque<Integer> deque = new LinkedList<>(); for (int i = 0; i < num.length; i++) { //Step1: 头: 移除头部, 保证窗口的长度范围 if (!deque.isEmpty() && (i - deque.getFirst()) >= size){ //这个条件也可以换成 deque.getFirst() < (i - (size - 1)) deque.removeFirst(); } //Step2: 尾: 移除尾部小于当前值的元素, 去除不可能的元素 while (!deque.isEmpty() && num[i] >= num[deque.getLast()]){ deque.removeLast(); } //Step3: 尾部加入, 滑动窗口向右扩充 deque.addLast(i); //Step4: 头, 从头部返回极大值 if (i >= size - 1){ list.add(num[deque.getFirst()]); //list.add(num[deque.peekFirst()]); //所有的dq.getFirst()也可以换为dq.peekFirst() } } return list; } }
参考: