题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
解法一:暴力法,时间复杂度O(nk)
- 扫描每个滑动窗口的所有数字并找出其中的最大值。
- 如果滑动窗口的大小为K,则需要O(k)时间找到最大值。
- 对于长度为 n 的输入数组,算法的时间复杂度为O(nk)。
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int>res;
if(num.size()<size)
return res;
for(int i = 0, j=size-1; i<num.size()-size+1 && j<num.size(); i++, j++)
{
int M = num[i];
for(int k = i; k<=j;k++) //遍历窗口的内所有的值,找出最大值。
{
M = max(M, num[k]);
}
res.push_back(M);
}
return res;
}
};
解法二:最大堆,时间复杂度O(nlogk)
由于要考虑最大堆堆顶数字是否超过了滑动窗口的范围,所以最大堆中存放pair类型,第一个为数组的数字,第二个是数组中数字对应的下标值。
- 采用最大堆结构
- 每次遍历时,向堆中插入对应的pair,并检查堆顶pair类型的数字是否超过华东窗口的范围。没有超出,则取堆顶pair类型的第一个位置的数据。
class Solution {
public:
vector<int> maxInWindows(vector<int>& nums, int k) {
vector<int>value;
if(nums.size()==0 || k<=0 || k>nums.size())
return value;
priority_queue<pair<int, int>>q;
for(int i=0; i<k; i++)
{
q.push(make_pair(nums[i], i));
}
value.push_back(q.top().first);
for(int i=k; i<nums.size(); i++)
{
while(i - q.top().second > k-1 && !q.empty())
q.pop();
q.push(make_pair(nums[i], i));
value.push_back(q.top().first);
}
return value;
}
};
解法三:双端队列(deque),时间复杂度O(n)
算法思想:借助一个辅助双端队列,步骤如下:
- 如果队列为空,则当前数字入队列。
- 如果队列不为空且当前数字大于等于队尾数字,则队尾数字出队直到队尾元素小于当前待入队的数字,或者在队尾数字出队的过程中,队列为空,则当前数字入队。
- 如果当前数字小于队尾数字,则当前数字入队尾。
- 如果队列头部数字超出滑动窗口的范围(比如,队列头部数字在数组中的位置是第一个,但是滑动窗口的范围在第二个到第四个,那么队列头部数字直接就可以删除),则删除队列头部数字(pop_front())。
由于要考虑队列头部数字是否超过了滑动窗口的范围,所以队列中存放的元素不能是原来的数组的数字,而应该是数组中数字的下标值。
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int>res;
if(num.size() < size || size <= 0)
return res;
deque<int>index;
for(unsigned int i=0; i<size; i++)
{
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
index.push_back(i);
}
for(unsigned int i=size; i<num.size(); i++)
{
res.push_back(num[index.front()]);
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
if(!index.empty() && index.front()<=(int)(i - size) )//如果队列头部数字超出滑动窗口的范围,则删除队列头部数字
index.pop_front();
index.push_back(i);
}
res.push_back(num[index.front()]);//滑到最后一个数字时,循环内部并没有执行读队头操作,所以这里要留意下。
return res;
}
};
或者可以做如下修改。只是调整了其中两行代码的顺序
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int>res;
if(num.size() < size || size <= 0)
return res;
deque<int>index;
for(unsigned int i=0; i<size; i++)
{
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
index.push_back(i);
}
res.push_back(num[index.front()]); // #####################
for(unsigned int i=size; i<num.size(); i++)
{
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
if(!index.empty() && index.front()<=(int)(i - size) )
index.pop_front();
index.push_back(i);
res.push_back(num[index.front()]);// ###########################
}
return res;
}
};