最大滑动窗口

参考:http://blog.csdn.net/ssjhust123/article/details/7967489


题目描述

给定一个数组A[],有一个大小为w的滑动窗口,该滑动窗口从最左边滑到最后边。在该窗口中你只能看到w个数字,每次只能移动一个位置。我们的目的是找到每个窗口w个数字中的最大值,并将这些最大值存储在数组B中。

例如数组A=[1 3 -1 -3 5 3 6 7], 窗口大小w为3。则窗口滑动过程如下所示:

Window position                Max
---------------               -----
[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

输入参数为数组A和w大小
输出为数组B,其中B[i]存储了A[i]到A[i+w-1]中w个数字的最大值。

一、简单的解法

一个最简单的想法就是每次移动都计算w个数字的最大值并保存起来,每次计算w个数字的最大值需要O(w)的时间,而滑动过程需要滑动n-w+1次,n为数组大小,因此总共的时间为O(nw)。代码如下:

[cpp]  view plain copy
  1. /*最大滑动窗口主函数*/  
  2. void maxSlidingWindow(int A[], int n, int w, int B[])   
  3. {  
  4.     for (int i=0; i<=n-w; i++)   
  5.         B[i] = max(A+i, w);  
  6. }  
  7.   
  8. /*求数组最大值*/  
  9. int max(int A[], int n)  
  10. {  
  11.     int max = A[0];  
  12.     for (int i=1; i<n; i++) {  
  13.         if (A[i] > max) {  
  14.             max = A[i];  
  15.         }  
  16.     }  
  17.     return max;  
  18. }  


二、最大堆解法

第1个方法思路简单,但是时间复杂度过高,因此需要改进。可以使用一个最大堆来保存w个数字,每次插入数字时只需要O(lgw)的时间,从堆中取最大值只需要O(1)的时间。随着窗口由左向右滑动,因此堆中有些数字会失效(因为它们不再包含在窗口中)。

[cpp]  view plain copy
  1. typedef pair<intint> Pair;  
  2. void maxSlidingWindow(int A[], int n, int w, int B[])  
  3. {  
  4.     priority_queue<Pair> Q; //优先级队列保存窗口里面的值  
  5.     for (int i = 0; i < w; i++)  
  6.         Q.push(Pair(A[i], i));  //构建w个元素的最大堆  
  7.     for (int i = w; i < n; i++) {  
  8.         Pair p = Q.top();  
  9.         B[i-w] = p.first;  
  10.         while (p.second <= i-w) {  
  11.             Q.pop();  
  12.             p = Q.top();  
  13.         }  
  14.         Q.push(Pair(A[i], i));  
  15.     }  
  16.     B[n-w] = Q.top().first;  
  17. }  
如果数组本身有序,则里面的while循环不会执行,堆大小会增大到n。 因为堆大小并不保持在w不变 ,因此该算法时间复杂度为O(nlgn)。


三、双向队列O(N)解法

最大堆解法在堆中保存有冗余的元素,比如原来堆中元素为[10 5 3],新的元素为11,则此时堆中会保存有[11 5 3]。其实此时我们可以清空整个队列,然后再将11加入到队列即可,即只在队列中保持[11]。使用双向队列可以满足要求,滑动窗口的最大值总是保存在队列首部队列里面的数据总是从大到小排列。当遇到比当前滑动窗口最大值更大的值时,则将队列清空,并将新的最大值插入到队列中。如果遇到的值比当前最大值小,则直接插入到队列尾部。每次移动的时候需要判断当前的最大值是否在有效范围,如果不在,则需要将其从队列中删除。由于每个元素最多进队和出队各一次,因此该算法时间复杂度为O(N)。

[cpp]  view plain copy
  1. void maxSlidingWindow(int A[], int n, int w, int B[])   
  2. {  
  3.   deque<int> Q;  
  4.   for (int i = 0; i < w; i++) {  
  5.     while (!Q.empty() && A[i] >= A[Q.back()])  
  6.       Q.pop_back();  
  7.     Q.push_back(i);  
  8.   }  
  9.   for (int i = w; i < n; i++) {  
  10.     B[i-w] = A[Q.front()];  
  11.     while (!Q.empty() && A[i] >= A[Q.back()])  
  12.       Q.pop_back();  
  13.     while (!Q.empty() && Q.front() <= i-w)  
  14.       Q.pop_front();  
  15.     Q.push_back(i);  
  16.   }  
  17.   B[n-w] = A[Q.front()];  
  18. }  
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值