题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
解:
首先处理一下特殊情况:如果输入数组为空或者滑动窗口长度小于等于0,那应该返回一个空数组。如果滑动窗口长度大于输入数组长度,也应该返回空数组。如果滑动窗口长度为1,那么直接返回输入数组即可。
接下来分析一下滑动的过程,可以用一个首尾开口的队列deque来模拟这个情况。首先处理元素2,放进队列,然后处理元素3,此时元素2不可能是窗口最大值,因此从队列尾部删除,处理元素4时也将元素3从队列尾部删除。此时第一个窗口已经完成,队列中只存在一个元素4,为当前窗口最大值
之后开始滑动窗口,处理第四个元素2,此元素在之后是有可能成为最大值的,因此放到队列尾部。此时最大值为4。继续处理第五个元素6,6比4和2都大,因此将队列清空,把6放进队列。此时最大值为6。继续将第六个元素2放进队列。第七个元素是5,5比2大,因此2不可能成为最大值,将2删除,5放进队列末尾。直到此时,窗口最大值依然为6。处理最后一个元素1,将此元素放进队列末尾。此时队列的最大值已经不在窗口里了,因此将队列头部元素删除,此时的队列头部元素为5,是窗口最大值。运算结束。
此法的原理为,在滑动窗口的过程中,如果遇到的某个数字比队列所有元素都大,则队列中所有元素都不可能成为之后窗口的最大值,因此队列只保留当前遇到的数字。否则比较一下其与队尾元素大小,如果其比队尾元素大,则删除队尾,将元素放入。
总之,队列中只会保留当前窗口中有可能成为最大值的元素。
同时,由于要判断当前最大值是否在窗口中,队列中保存的不是元素值,而是数组下标。
代码如下:
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> Res;
int Len = num.size();
if(Len==0||size==0||size>Len) return Res;
deque<int> Index;//两端开口队列,存储元素下标
Index.push_back(0);
//先将窗口内的元素处理完毕,完成后队列中的元素都有可能成为窗口最大值
for(int i = 1; i < size; i++){
if(num[i]>num[Index.front()]){
while(!Index.empty()) Index.pop_back();
}
Index.push_back(i);
}
Res.push_back(num[Index.front()]);
for(int i = size; i < Len; i++){
//当元素比队列头部大,则队列清空,只存放当前元素
if(num[i]>num[Index.front()]){
while(!Index.empty()) Index.pop_back();
}
else {
//否则判断先前最大值是否在窗口中,如果不在,则删除
if(i-Index.front()>=size) Index.pop_front();
//判断元素是否比队列尾部大,如果是则删除
while(num[i]>num[Index.back()]) Index.pop_back();
}
Index.push_back(i);
Res.push_back(num[Index.front()]);
}
return Res;
}
};
题目来源:https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788?tpId=13&tqId=11217&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking&tPage=4