每日一题 - 剑指 Offer 59 - I. 滑动窗口的最大值
Posted id-wangqiang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一题 - 剑指 Offer 59 - I. 滑动窗口的最大值相关的知识,希望对你有一定的参考价值。
题目信息
-
时间: 2019-07-05
-
题目链接:Leetcode
-
tag: 队列 双端队列 滑动窗口
-
难易程度:困难
-
题目描述:
给定一个数组
nums
和滑动窗口的大小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. 你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
解题思路
本题难点
数组无序,窗口内的最大值。
具体思路
双向队列:在头部进行插入、删除操作,也能在尾部进行插入删除操作。
单调的双向队列:从队列的头部到尾部,所存储的元素是依次递减(或依次递增)的。
我们维护一个单调的双向队列
,窗口在每次滑动的时候,我就从队列头部
取当前窗口中的最大值
,每次窗口新进来一个元素的时候,我就将它与队列中的元素进行大小比较:
- 进来的元素 > 队列的尾部元素:那么先将队列尾部的元素弹出,然后把刚刚进来的元素添到队列的尾部;
- 进来的元素 < 队列的尾部元素:那么把刚刚进来的元素直接添到队列的尾部即可。
删除元素:当队列的大小超过窗口的大小时,将队列的最大值弹出,窗口进行滑动。
代码
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || k == 0){
return new int[0];
}
Deque<Integer> deque = new LinkedList<>();
int[] res = new int[nums.length - k + 1];
int count = 0;
for(int i = 0; i < nums.length; i++){
// 在队列不为空的情况下,如果队列尾部的元素要比当前的元素小,或等于当前的元素
// 那么为了维持从大到小的原则,我必须让尾部元素弹出
while(!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]){
deque.pollLast();
}
// 不走 while 的话,说明我们正常在队列尾部添加元素
deque.add(i);
// 如果滑动窗口已经略过了队列中头部的元素,则将头部元素弹出
if(deque.peekFirst() == (i - k)){
deque.pollFirst();
}
// 看看窗口有没有形成,只有形成了大小为 k 的窗口,我才能收集窗口内的最大值
if(i >= k-1){
res[count++] = nums[deque.peekFirst()];
}
}
return res;
}
}
复杂度分析:
- 时间复杂度 O(N) : 其中 n 为数组 nums 长度;线性遍历 nums 占用 O(N) ;每个元素最多仅入队和出队一次,因此单调队列 deque 占用 O(2N) 。
- 空间复杂度 O(k) : 双端队列 deque 中最多同时存储 k个元素(即窗口大小)。
其他优秀解答
解题思路
改善之后的暴力法
代码
public int[] maxSlidingWindow(int[] nums, int k) {
int len = nums.length;
if (len == 0){
return new int[0];
}
//定义结果数组
int[] res = new int[len - k + 1];
//maxInd记录每次最大值的下标,max记录最大值
int maxInd = -1, max = Integer.MIN_VALUE;
for (int i = 0; i < len - k + 1; i++) {
//判断最大值下标是否在滑动窗口的范围内
if (maxInd >= i){
//存在就只需要比较最后面的值是否大于上一个窗口最大值
if (nums[i + k - 1] > max){
max = nums[i + k - 1];
//更新最大值下标
maxInd = i + k - 1;
}
}
//如果不在就重新寻找当前窗口最大值
else {
max = nums[i];
for (int j = i; j < i + k; j++) {
if (max < nums[j]) {
max = nums[j];
maxInd = j;
}
}
}
res[i] = max;
}
return res;
}
以上是关于每日一题 - 剑指 Offer 59 - I. 滑动窗口的最大值的主要内容,如果未能解决你的问题,请参考以下文章
每日一题 - 剑指 Offer 56 - I. 数组中数字出现的次数
每日一题 - 剑指 Offer 53 - I. 在排序数组中查找数字 I
每日一题 - 剑指 Offer 59 - II. 队列的最大值
乱序版 ● 剑指offer每日算法题打卡题解——栈与队列(题号59)
LeetCode664. 奇怪的打印机 / 剑指 Offer 55 - I. 二叉树的深度 / 剑指 Offer 55 - II. 平衡二叉树
剑指 Offer 53 - I. 在排序数组中查找数字 I / 剑指 Offer 42. 连续子数组的最大和(线段树基础)/152. 乘积最大子数组 / 面试题 10.02. 变位词组