前言
一、剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组 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
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
二、解决方法
1.引入库
代码如下:
方法一
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
if(k == 1) return nums;
int n = nums.size();
vector<int> res;
if(n == 0) return res;
res.resize(n-k+1);
res[0] = nums[0];
for(int i = 1;i<k;i++)
{//计算 0 到 k 的最大值存入res[0]
res[0] = max(res[0],nums[i]);
}
for(int i = k;i<n;i++)
{
if(res[i-k] == nums[i-k])
{
res[i-k+1] = nums[i];
for(int j = i-k+1;j<i;j++)
{
res[i-k+1] = max(nums[j],res[i-k+1]);
}
}
else res[i-k+1] = max(res[i-k],nums[i]);
}
return res;
}
};
作者:an-an-jing-jing-6
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/yong-shi-8-ms-ji-bai-liao-9995-nei-cun-1-miqq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二 双向队列(容易理解 )
解题思路
关键就是用双向队列模拟实现了滑动窗口的过程,同时将这个队列维护成了一个单调队列
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int>res;
deque<int>dp;
int len = nums.size();
if(k<=0 || len<=0 || len<k)
return res;
for(int i=0;i<len;i++){
while(!dp.empty() && nums[i] >= nums[dp.back()]){
dp.pop_back();//将队列中比刚进入的值小的值弹出去,因为最大值不可能在他们中间产生
}
dp.push_back(i);//将这个值加入到单调队列中合适的位置
if(i >= k-1){//从第一个窗口长度开始处理
res.push_back(nums[dp.front()]);
if(i-k+1 == dp.front()){//如果现在的最值等于窗口的第一个值,那么要弹出
dp.pop_front();
}
}
}
return res;
}
};
作者:gnu-z
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/hua-dong-chuang-kou-de-zui-da-zhi-by-gnu-diy6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法三 单调队列
思路:
因为题目要求返回滑动窗口的最大值,而滑动窗口的实质相当于一个队列,每滑动一次都会有窗口左端(相当于队首)的元素出队,窗口右端(相当于队尾)有新的元素入队。
题目包含min函数的栈为通过维护一个单调栈来实现以O(1)的时间复杂度来返回最小值,可以发现实质与该题类似,不过是把栈变成了队列,并且把返回最小值换成了返回最大值。
因此考虑维护一个非严格的单调增的队列来实现最大值的返回。因为滑动窗口会导致队首元素出队,因此考虑使用双端队列实现单调增队列。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
if(nums.empty()) // 特殊情况的处理
return res;
deque<int> deQ; // 维护一个双端队列
/* 滑动窗口大小小于k时*/
for(int i=0; i<k; i++){
while(!deQ.empty() && deQ.back()<nums[i]) // 始终保持队首元素为最大值
deQ.pop_back();
deQ.push_back(nums[i]);
}
res.push_back(deQ.front());
/* 形成大小k的滑动窗口 */
for(int i=k; i<nums.size(); i++){
if(deQ.front() == nums[i-k])
deQ.pop_front();
while(!deQ.empty() && deQ.back() < nums[i]) // 始终保持队首元素为最大值
deQ.pop_back();
deQ.push_back(nums[i]);
res.push_back(deQ.front());
}
return res;
}
};
作者:yi-ban-shi-min-young
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/cdan-diao-dui-lie-by-yi-ban-shi-min-youn-yndk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2.解题思路(链接视频讲解)
1.想将我们第一个窗口的所有值存入单调双端队列中,单调队列里面的值为单调递减的。如果发现队尾元素小于要加入的元素,则将队尾元素出队,直到队尾元素大于新元素时,再让新元素入队,目的就是维护一个单调递减的队列。
2.我们将第一个窗口的所有值,按照单调队列的规则入队之后,因为队列为单调递减,所以队头元素必为当前窗口的最大值,则将队头元素添加到数组中。
3.移动窗口,判断当前窗口前的元素是否和队头元素相等,如果相等则出队。
4.继续然后按照规则进行入队,维护单调递减队列。
5.每次将队头元素存到返回数组里。
5.返回数组
作者:chefyuan
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/yi-shi-pin-sheng-qian-yan-shuang-duan-du-mbga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。