認真對待每一道算法題 之 滑動窗口的最大值


給你一個滑動窗口和一個數組,滑動窗口從數組第一個元素開始向后滑動,每滑動一下就計算當前窗口中對應的數組元素的最大值;

設置窗口長度為m,數組長度為n,有O(n*m)算法,用最大堆的O(n*lgm)算,利用已經比較過的元素之間關系的O(n)算法;

 

摘自博客 http://blog.csdn.net/sgbfblog/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. }  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM