双端队列
题目详情
给你一个整数数组 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)
{
deque<int> dq; //双端队列(保存递减元素的索引而非元素值)
vector<int> ans; //答案vector
//遍历nums
//注意要实现滑动窗口,就要保证每次窗口内的元素个数都为k个
for (int i = 0; i < nums.size(); ++i)
{
//下面的第一个if和while用来实现两个筛选条件--队首出队+队右小元素出队
//第一次不会执行因为dq为空
//dq.front()(最大值在nums中的索引)
//dq.front() == i - k说明当前以i结尾的滑动窗口已经不包括dq.front()了(这里我迷糊了一下)
//因为如果包括的话,就应该是dq.front()+1 == i - k
//例如假设最大值索引为0,现在遍历到了末尾索引3的,那么3-3=0但是窗口中却已经有0123四个元素了
//所以需要满足dq.front()+1 == i - k
if (!dq.empty() && dq.front() == i - k)
{
dq.pop_front(); //窗口后移了,最大值索引不在窗口中了,故让其出队
}
//队尾往前while,小于新窗口右端(新push的元素)的都出队
//(保证新元素下标插入队列位置正确--左边没有小于它的)
while (!dq.empty() && nums[dq.back()] < nums[i])
{
dq.pop_back();
}
dq.push_back(i); //将新元素下标插入队列
//i >= k - 1是为了确保 dq.front() 至少为第一个完整的滑动窗口的最大值索引。
//即至少初次形成了一个完整的滑动窗口。
if (i >= k - 1)
{
ans.push_back(nums[dq.front()]); //每次循环都记录一次队首即为最大值
}
}
return ans;
}
};
本题关键在于我们要用双端队列模拟出一个队首始终记录最大元素,
怎么实现呢,那就每次移动窗口对应两种筛选条件:
1.当队首不在滑动窗口中时,front.pop()
2.每次push元素都要看前面有没有比其小的元素从而将其pop_back
这样双端队列中始终是递减的顺序
因为我们要执行pop和push,还要在nums中提取最大的元素存入ans
所以我们选择利用deque存储元素的索引而非元素本身