滑动窗口的最大值
题目描述
来源:https://leetcode-cn.com/problems/sliding-window-maximum/
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
1 <= k <= nums.length
思路分析
一、暴力求解,一层循环遍历n-k+1
次,二层循环遍历k的长度,时间复杂度为O(NK),存在的问题就是在窗口移动的过程中,存在元素重复的情况,k很大的时候非常明显效率很低。
二、使用单调的双向队列,维护的队列支持双端操作,元素在入队出队的时候保证O(1)的时间操作,并且我们需要淘汰掉永远不可能是当前的答案的元素。
具体代码
暴力求解
//暴力解 时间复杂度上面 (n-k+1) * k 存在问题,大量的重复比较
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if (n * k == 0) return new int[0];
int[] res = new int[n - k + 1];
for (int i = 0; i < n - k + 1; i++) {
int max = Integer.MIN_VALUE;
for (int j = i; j <= i + k - 1; j++)
max = Math.max(max, nums[j]);
res[i] = max;
}
return res;
}
双向队列
//单调双端队列 deque o(n) 单词滑动窗口获取最大值 o(1) max --> min
//将永远没有机会的消除, 减少重复的比较
public int[] solve(int[] nums, int k) {
int n = nums.length;
if (n * k == 0) return new int[0];
int[] res = new int[n - k + 1];
//存储index 1、容易改变滑动窗口的范围, 2、可以对数组中的值进行操作
Deque<Integer> dq = new LinkedList<>();
for(int i = 0; i < n ; i++){
//移除头部,保证窗口的长度范围
if(!dq.isEmpty() && dq.getFirst() < (i-k+1)){
dq.removeFirst();
}
//移除尾部小于当前值的元素
while(!dq.isEmpty() && nums[i] >= nums[dq.getLast()]){
dq.removeLast();
}
//尾部加入,滑动窗口向右扩充
dq.addLast(i);
//从头部返回极大值
if(i >= k - 1){
res[i -k +1] = nums[dq.getFirst()];
}
}
return res;
}