給你一個滑動窗口和一個數組,滑動窗口從數組第一個元素開始向后滑動,每滑動一下就計算當前窗口中對應的數組元素的最大值;
設置窗口長度為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)。代碼如下:
- /*最大滑動窗口主函數*/
- void maxSlidingWindow(int A[], int n, int w, int B[])
- {
- for (int i=0; i<=n-w; i++)
- B[i] = max(A+i, w);
- }
- /*求數組最大值*/
- int max(int A[], int n)
- {
- int max = A[0];
- for (int i=1; i<n; i++) {
- if (A[i] > max) {
- max = A[i];
- }
- }
- return max;
- }
二、最大堆解法
第1個方法思路簡單,但是時間復雜度過高,因此需要改進。可以使用一個最大堆來保存w個數字,每次插入數字時只需要O(lgw)的時間,從堆中取最大值只需要O(1)的時間。隨着窗口由左向右滑動,因此堆中有些數字會失效(因為它們不再包含在窗口中)。
- typedef pair<int, int> Pair;
- void maxSlidingWindow(int A[], int n, int w, int B[])
- {
- priority_queue<Pair> Q; //優先級隊列保存窗口里面的值
- for (int i = 0; i < w; i++)
- Q.push(Pair(A[i], i)); //構建w個元素的最大堆
- for (int i = w; i < n; i++) {
- Pair p = Q.top();
- B[i-w] = p.first;
- while (p.second <= i-w) {
- Q.pop();
- p = Q.top();
- }
- Q.push(Pair(A[i], i));
- }
- B[n-w] = Q.top().first;
- }
如果數組本身有序,則里面的while循環不會執行,堆大小會增大到n。因為堆大小並不保持在w不變,因此該算法時間復雜度為O(nlgn)。
三、雙向隊列O(N)解法
最大堆解法在堆中保存有冗余的元素,比如原來堆中元素為[10 5 3],新的元素為11,則此時堆中會保存有[11 5 3]。其實此時我們可以清空整個隊列,然后再將11加入到隊列即可,即只在隊列中保持[11]。使用雙向隊列可以滿足要求,滑動窗口的最大值總是保存在隊列首部,隊列里面的數據總是從大到小排列。當遇到比當前滑動窗口最大值更大的值時,則將隊列清空,並將新的最大值插入到隊列中。如果遇到的值比當前最大值小,則直接插入到隊列尾部。每次移動的時候需要判斷當前的最大值是否在有效范圍,如果不在,則需要將其從隊列中刪除。由於每個元素最多進隊和出隊各一次,因此該算法時間復雜度為O(N)。
- void maxSlidingWindow(int A[], int n, int w, int B[])
- {
- deque<int> Q;
- for (int i = 0; i < w; i++) {
- while (!Q.empty() && A[i] >= A[Q.back()])
- Q.pop_back();
- Q.push_back(i);
- }
- for (int i = w; i < n; i++) {
- B[i-w] = A[Q.front()];
- while (!Q.empty() && A[i] >= A[Q.back()])
- Q.pop_back();
- while (!Q.empty() && Q.front() <= i-w)
- Q.pop_front();
- Q.push_back(i);
- }
- B[n-w] = A[Q.front()];
- }