题目如下:
给出一个可能包含重复的整数数组,和一个大小为 k 的滑动窗口, 从左到右在数组中滑动这个窗口,找到数组中每个窗口内的最大值。O(n)时间,O(k)的额外空间
样例
给出数组 [1,2,7,7,8]
, 滑动窗口大小为 k = 3
. 返回 [7,7,8]
.
解释:
最开始,窗口的状态如下:
[|1, 2 ,7| ,7 , 8]
, 最大值为 7
;
然后窗口向右移动一位:
[1, |2, 7, 7|, 8]
, 最大值为 7
;
最后窗口再向右移动一位:
[1, 2, |7, 7, 8|]
, 最大值为 8
.
解题思路:
O(n)的复杂度明显需要缓存,每个窗口间都存在并集部分,那么并集部分的最大值当然就是缓存了。至于遍历,我们可以像下图一样(m1,m2,m3为所在窗口最大值):
由于我们需要的只是缓存里的最大值,只要在保证缓存的最大值在窗口有效区间内,那么该最大值也就是该窗口的最大值了。显然,排序缓存是不可靠的提取最大值方法,除开复杂度提升问题不说,缓存里存储的数组下标也跟排序不相衬,并且缓存里的非区间最大值也许永远都用不上,因此,我们采取压榨区间来提取最大值。采用双向列表的优势,不停把最后元素与目前遍历处理的元素相比较,少于则popback,直到列表back元素大于它,大于则pushback,这样,我们可以确保,列表任意两个元素组成的区间内的元素都比这两个元素要小。只要列表最前元素在区间内,此元素也就是该区间最大值,不在,则下一个元素是最大值。
思路代码实现如下:
void Method(int *s,int len,int k)
{
deque<short>d;int i;
for(i=0;i<len;++i)
{
if(!d.empty()&&d.front()==i-k)
d.pop_front();
while(!d.empty()&&d.back()<s[i])
d.pop_back();
d.push_back(i);
if(i>=k-1)
printf("%d ",s[d.front()]);
}
}