题目如下
给定一个数组 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
解题思路
我们可以采用队列,DP,堆等方式进行求解,所有思路的主要源头应该都是在窗口滑动的过程中,如何更快的完成查找最大值的过程。但是最典型的解法还是使用双端队列。具体怎么来求解,一起看一下。
首先,我们了解一下,什么是双端队列:是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出或者插入。
我们可以利用双端队列来实现一个窗口,目的是让该窗口可以做到张弛有度(汉语博大精深,也就是长度动态变化。其实用游标或者其他解法的目的都是一样的,就是去维护一个可变长的窗口)
然后我们再做一件事,只要遍历该数组,同时在双端队列的头去维护当前窗口的最大值(在遍历过程中,发现当前元素比队列中的元素大,就将原来队列中的元素删除出队),在整个遍历的过程中我们再记录下每一个窗口的最大值到结果数组中。最终结果数组就是我们想要的,整体图解如下。
假设 nums = [1,3,-1,-3,5,3,6,7],和 k = 3
解题代码
#include <iostream>
#include <vector>
#include <deque>
using namespace std;
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> result;
if (num.size() >= size && size >= 1)
{
deque<int> numDeque;
//首先把前size个数按照规则压入双向队列
for (int i = 0; i != size; i++)
{
while (!numDeque.empty() && num[i] >= num[numDeque.back()])
{
numDeque.pop_back();
}
numDeque.push_back(i);
}
//压入第一个最大值
//滑动窗口的最大值总是位于双向队列的头部
result.push_back(num[numDeque.front()]);
for (int i = size; i != num.size(); i++)
{
//首先按照规则压入新的值
while (!numDeque.empty() && num[i] >= num[numDeque.back()])
{
numDeque.pop_back();
}
//并且删除旧值,即滑出了窗口的值
if (!numDeque.empty() && numDeque.front() <= static_cast<int>(i - size))
{
numDeque.pop_front();//出队
}
numDeque.push_back(i);
result.push_back(num[numDeque.front()]);
}
}
return result;
}
int main()
{
vector<int> data{ 2,3,4,2,6,2,5,1 };
vector<int> result = maxInWindows(data, 3);
for (auto a : result)
{
cout << a << " ";
}
cout << endl;
return 0;
}