目录
问题描述:
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入: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
示例 2:
输入:nums = [1], k = 1 输出:[1]
实现代码和解析:
暴力法(会超时):
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k)
{
vector<int> result;
//移动
for(int i=0;i<nums.size()-k+1;i++)
{
int max=INT_MIN;
for(int j=i;j<i+k;j++)
{
if(nums[j]>max)
{
max=nums[j];
}
}
result.push_back(max);//放入结果数组中
}
return result;
}
};
原理思路:
注意此方法会超时,就是直接暴力循环把每个窗口的最大值都遍历比较出来。
单调队列法:
//实现单调队列
class Myque
{
public:
deque<int> que;//deque来实现单调队列
//入单调队列
void push(int n)
{
//将队列中比要入队列的元素小的全部弹出
while(!que.empty()&&que.back()<n)
{
que.pop_back();
}
que.push_back(n);
}
//出单调队列
void pop(int n)
{
//若本来该出队列的元素未在其他元素入队列的时候弹出,则将它出队列
if(!que.empty()&&que.front()==n)
{
que.pop_front();
}
}
//获得最大值
int front()
{
return que.front();
}
};
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k)
{
Myque que;
vector<int> result;//接收结果
//先将前k个数入单调队列
for(int i=0;i<k;i++)
{
que.push(nums[i]);
}
result.push_back(que.front());//记录前k个的最大值
//i指向右窗口端开始移动
for(int i=k;i<nums.size();i++)
{
que.pop(nums[i-k]);//将出窗口的元素出队列
que.push(nums[i]);//将进入窗口的元素入队列
result.push_back(que.front());//记录当前窗口最大值
}
return result;
}
};
原理思路:
利用单调队列的经典题。下面就介绍一下单调队列。
单调队列:
单调队列就是利用改变入队列和出队列的规则,使队列单调递增或递减,这里就是让最大值永远在队头(单调队列的实现不是固定的,需要根据具体的题)。
首先要知道什么是deque,也是是双端队列,就是两端都可以入队列和出队列的一种结构。然后讲讲具体实现,我们用双端队列来实现单调队列,改变其出队列和入队列的规则。
入队列:将队列中后面小于入队列元素的全部出队列,注意这里用的是while删除多个,而且是小于的,不能把等于的也出队列,因为我们在出队列时会判断出窗口元素是否会与队列头元素相同,如果把等于的也出队列,在某些情况下,会在出队列时移出本应该留在队列的元素。
//入单调队列
void push(int n)
{
//将队列中比要入队列的元素小的全部弹出
while(!que.empty()&&que.back()<n)
{
que.pop_back();
}
que.push_back(n);
}
出队列:将出滑动窗口的元素移出队列,要判断一下移出元素与队头元素是否相等,若相等才出队列,因为不相等的话,说明此元素已经在我们入队列的时候移出队列了。
//出单调队列
void pop(int n)
{
//若本来该出队列的元素未在其他元素入队列的时候弹出,则将它出队列
if(!que.empty()&&que.front()==n)
{
que.pop_front();
}
}
获取最大值:其实就是返回队列头元素,因为我们实现了单调队列嘛。
//获得最大值
int front()
{
return que.front();
}
具体解题:我们先将前k个元素入队列,然后找出最大值记录在result数组中,然后我们开始移动窗口,在移动时入队列,出队列,记录每个窗口的最大值,这样我们就得到了结果。
//先将前k个数入单调队列
for(int i=0;i<k;i++)
{
que.push(nums[i]);
}
result.push_back(que.front());//记录前k个的最大值
//i指向右窗口端开始移动
for(int i=k;i<nums.size();i++)
{
que.pop(nums[i-k]);//将出窗口的元素出队列
que.push(nums[i]);//将进入窗口的元素入队列
result.push_back(que.front());//记录当前窗口最大值
}
模拟过程:
看完如果还是不太明白为什么这样就能实现获取每个窗口的最大值,那我给出一个例子模拟一下过程,大家就能明白了,下面给出。