LeetCode 239. 滑動視窗最大值 java實現 個人演算法之旅筆記
239. 滑動視窗最大值
給定一個數組 nums,有一個大小為 k 的滑動視窗從陣列的最左側移動到陣列的最右側。你只可以看到在滑動視窗 k 內的數字。滑動視窗每次只向右移動一位。
返回滑動視窗最大值。
示例:
輸入: nums =[1,3,-1,-3,5,3,6,7]
, 和 k = 3 輸出:[3,3,5,5,6,7] 解釋:
滑動視窗的位置 最大值 --------------- ----- [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
注意:
你可以假設 k 總是有效的,1 ≤ k ≤ 輸入陣列的大小,且輸入陣列不為空。
解法一(時間複雜度為O(n*logK)):返回視窗中的的最大值,最大最小值我們可以優先考慮到“優先佇列”,優先佇列用於流式資料的最大最小值。演算法題中,有出現slide window的都是高頻考點
a.維持一個max heap(刪除滑動視窗最左邊的元素,加入新的元素)
b.讓最大值位於大頂堆
a步驟的維持max head的時間複雜度是logK。
b步驟的時間複雜度是N *O(1),即O(N)。
總體時間複雜度為O(n*logK)。
class Solution { public int[] maxSlidingWindow(int[] nums, int k) { if(!(nums instanceof int[]) || nums == null || nums.length == 0)//判斷傳進來的是否為int陣列,int陣列是否為空,int陣列是否沒有資料 return new int[0]; PriorityQueue<Integer> pq = new PriorityQueue<Integer>(Collections.reverseOrder()); int[] max = new int[nums.length + 1 - k];//nums.length + 1 - k得出該陣列nums裡面滑動視窗的最大值的個數 for(int i = 0 ;i < nums.length; i++){//遍歷 //先判斷是否為滑動視窗的最左邊,是的話移出優先佇列 if(i > k){//i > k ,表示當前滑動窗口裡面有K個數,想要新進來一個數,需要移除滑動視窗最左邊的數 pq.remove(nums[i -k]); } pq.offer(nums[i]);//新增資料 //獲取移動視窗的最大值 if(i + 1 >= k){//i + 1 >= k,當前滑動窗口裡面有K個數,所有才會有最大值 max[i + 1 - k] = pq.peek();//表示當前的max的陣列下標i + 1 - k } } return max; } }
解法二(時間複雜度為O(N)):使用雙端佇列。java中的雙端佇列deque(支援在兩端插入和移除元素)。deque是一個介面,實現它的類有ArrayDeque,LinkedBlockingDeque,LinkedList.
我們用雙向佇列,在遇到新的數的時候,將新的數和雙向佇列的末尾進行比較,如果末尾的數比新數下,則把末尾的數扔掉,直到該佇列的末尾數比新數大或者佇列為空的時候才停止。這樣,我們可以保證佇列裡的元素是重頭到位的降序。由於佇列中只有窗口裡的數,就是窗口裡的第一大數,第二大數,第三數...。
如何保持佇列呢。每當滑動視窗的k已滿,想要新進來一個數,就需要把最滑動視窗最左邊的數移出佇列,新增新的數。
我們在新增新的數的時候,就已經移出了一些數,這樣佇列頭部的數不一定是視窗最左邊的數。技巧:我們佇列中只儲存那個數在原陣列的下標。這樣可以判斷該數是否為最滑動視窗的最左邊的數。
為什麼這個解法的時間複雜度是O(N)呢。因為每個元素在雙端佇列裡有且僅存在過一次。即最多被操作兩次,一次是加入該佇列的時候,一次是因為後面有更大的數而被移除佇列的時候
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(!(nums instanceof int[]) || nums == null || nums.length == 0)//判斷傳進來的是否為int陣列,int陣列是否為空,int陣列是否沒有資料
return new int[0];
ArrayDeque<Integer> adq = new ArrayDeque<Integer>(k);
int[] max = new int[nums.length + 1 - k];//獲得該nums陣列滑動視窗的個數
for(int i = 0; i < nums.length; i++){
//每當新數進來,如果發現佇列的頭部的數的下標是視窗最左邊的下標,則移出佇列
if(!adq.isEmpty() && adq.peekFirst() == i - k)
adq.removeFirst();
//把佇列尾部的數和新數一一比較,比新數小的都移出佇列,直到該佇列的末尾數比新數大或者佇列為空的時候才停止,保證佇列是降序的
while(!adq.isEmpty() && nums[adq.peekLast()] < nums[i])
adq.removeLast();
//從尾部加入新的數
adq.offerLast(i);
//佇列頭部就是該視窗最大的數
if(i + 1 >= k)//i+1 <k時,滑動窗口才有最大值
max[i + 1 -k] = nums[adq.peek()];
}
return max;
}
}
其實我不知道該怎麼編輯,我只是想把自己學過的寫下來,雖然我很菜,可是我想變強。