題目:給定一個數組和滑動窗口的大小,找出所有滑動窗口里數值的最大值。例如,如果輸入數組{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]}。
思路:
如果用暴力搜索算法,毫無疑問,時間復雜度為O(nk)。我們可以開辟一個雙端隊列來存儲有可能成為滑動窗口最大值的數的下標,使當前滑動窗口的最大值為隊列的第一個元素,每次從數組中取出一個元素與隊列中尾端元素進行比較,前者大於后者則移除后者,往隊列尾端加入前者,前者小於后者就直接加入隊列尾端。
以數組{2,3,4,2,6,2,5,1}為例講解。數組第一個數是2,存入隊列,第二個數是3,3大於2,把2移除,存入3,第三個數是4,4大於3,把3移除,存入4,此時隊列的元素為{4};
第四個數是2,2小於4,當4滑出滑動窗口之后,2有可能成為最大值,把2存入隊列尾部,此時隊列元素為{4,2};
第五個數是6,6大於隊列中的2和4,因此2和4已經不可能成為滑動窗口中的最大值,從隊列中移除2和4;
第六個數是2,2小於6,存入隊列,此時隊列元素為{6,2};
第七個元素是5,5與隊列的尾端比較,5大於2,移除2,5小於6,把5存入隊列尾端,此時隊列元素為{6,5};
最后一個元素是1,1小於5,入隊列,但此時6已經不在滑動窗口中了,移除6,隊列中元素為{5,1}。
如何判斷一個數是否在滑動窗口中呢?
這就是為什么把數字在數組中的下標存到數組而不是把數字存到數組的原因,當一個數字的下標與當前處理的數字的下標之差大於或等於滑動窗口的大小時,這個數字已經從滑動窗口中滑出,可以從隊列中移除了。
這種算法的時間復雜度為O(n)。
參考代碼:
import java.util.ArrayList; import java.util.LinkedList; public class Solution { public ArrayList<Integer> maxInWindows(int [] num, int size) { ArrayList<Integer> result = new ArrayList<Integer>(); if(num == null || num.length == 0 || size == 0)return result; LinkedList<Integer> queue = new LinkedList<Integer>();
//初始化 for(int i=0;i<size-1;i++){ while(!queue.isEmpty() && num[i] > num[queue.getLast()]){ queue.removeLast(); } queue.addLast(i); } for(int i=size-1;i<num.length;i++){ while(!queue.isEmpty() && num[i] > num[queue.getLast()]){ queue.removeLast(); } queue.addLast(i);
//數字是否在滑動窗口中 if (i-queue.getFirst()+1 > size) queue.removeFirst(); result.add(num[queue.getFirst()]); } return result; } }