给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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,什么都不做;
滑动窗口大小超过数组大小,什么都不做;
数组元素个数为0,什么都不做;
以上两点需要额外注意!!!
1.0 版本
时间复杂度是O(n*size)。空间复杂度是O(n)。
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> res;
unsigned n = num.size();
if(0 == size || n < size) return res;
unsigned max = 0; //max用于记录当前窗口最大元素下标,靠右
for(unsigned i = 0; i <= n - size; ++i) //循环变量i用于控制窗口个数
{
max = max >= i ? max : i; //更新下标的值很重要
for(unsigned j = max; j < n && j <= i+size-1; ++j){
if(num[max] <= num[j]) max = j;
}
res.push_back(num[max]);
}
return res;
}
1.1 版本
相对于1.0版本,优化主要体现在某类情况——如果上一个窗口最大值元素还在在一个窗口中,那么只需要比较上一个窗口最大值元素和新加入的元素值大小,而不需要比较全部。
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
unsigned n = num.size(); //测得数组元素个数;
vector<int> res;
/*守卫代码 处理特例:
*数组元素个数为0;
*窗口大小为0;
*窗口大小大于数组元素个数;
*/
if(0 == n || 0 == size || n < size) return res;
//找到初始窗口中最大值所对应的下标;
unsigned indexMax = 0;
unsigned i;
for(i = 1; i < size; ++i){
if(num[i-1] <= num[i]) indexMax = i;
}
res.push_back(num[indexMax]);
for(i = 1; i <= n-size; ++i){
//if else 语句用于找到合适的indexMax;
if(indexMax >= i){ //如果上一次保留的indexMax处于当前窗口中;
if(num[indexMax] <= num[i+size-1])
indexMax = i+size-1;
}
else{ //如果上一次保留的indexMax不在当前窗口中;
//需要遍历整个窗口获取最大值及其下标;
indexMax = i;
for(int j = i+1; j <= i+size-1; ++j)
if(num[j-1] <= num[j])indexMax = j;
}
res.push_back(num[indexMax]);
}
return res;
}
2.0 版本
经过前两个版本的讨论,大道至简。思路如下:使用一个双端队列用于记录数组下标,保证队首元素一定是当前窗口的最大值。将数组遍历一次,每次根据队首元素找到数组元素,保存下来。
时间复杂度为O(n),空间复杂度为O(n)。
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> res;
if(0 == num.size() || 0 == size || num.size() < size) return res;
deque<unsigned> dq; //用于存放数组下标
for(unsigned i = 0; i < num.size(); ++i){
//剔除当前窗口较小值
while(dq.size() && num[dq.back()] <= num[i]) dq.pop_back();
//队头元素表示的下标不在窗口范围内
while(dq.size() && i-dq.front()+1 > size) dq.pop_front();
dq.push_back(i);
if(i+1 >= size) res.push_back(num[dq.front()]);
}
return res;
}