Leetcode 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 ≤ 輸入數組的大小,且輸入數組不為空。
進階:
你能在線性時間復雜度內解決此題嗎?
思路
維護一個大小為K的最大堆,依此維護一個大小為K的窗口,每次讀入一個新數,都把堆中窗口最左邊的數扔掉,再把新數加入堆中,這樣堆頂就是這個窗口內最大的值。
註意
-結果數組的大小是nums.length + 1 - k, 賦值時下標也是i + 1 - k
1 import java.util.Collections; 2 import java.util.PriorityQueue;3 4 public class Solution { 5 public int[] maxSlidingWindow(int[] nums, int k) { 6 if(nums == null || nums.length == 0) return new int[0]; 7 PriorityQueue<Integer> pq = new PriorityQueue<Integer>(Collections.reverseOrder()); 8 int[] res = new int[nums.length + 1 - k];9 for(int i = 0; i < nums.length; i++){ 10 // 把窗口最左邊的數去掉 11 if(i >= k) pq.remove(nums[i - k]); 12 // 把新的數加入窗口的堆中 13 pq.offer(nums[i]); 14 // 堆頂就是窗口的最大值 15 if(i + 1 >= k) res[i + 1 - k] = pq.peek(); 16 } 17 return res; 18 } 19 }
雙向隊列
復雜度
時間 O(N) 空間 O(K)
思路
我們用雙向隊列可以在O(N)時間內解決這題。當我們遇到新的數時,將新的數和雙向隊列的末尾比較,如果末尾比新數小,則把末尾扔掉,直到該隊列的末尾比新數大或者隊列為空的時候才住手。這樣,我們可以保證隊列裏的元素是從頭到尾降序的,由於隊列裏只有窗口內的數,所以他們其實就是窗口內第一大,第二大,第三大...的數。保持隊列裏只有窗口內數的方法和上個解法一樣,也是每來一個新的把窗口最左邊的扔掉,然後把新的加進去。然而由於我們在加新數的時候,已經把很多沒用的數給扔了,這樣隊列頭部的數並不一定是窗口最左邊的數。這裏的技巧是,我們隊列中存的是那個數在原數組中的下標,這樣我們既可以直到這個數的值,也可以知道該數是不是窗口最左邊的數。這裏為什麽時間復雜度是O(N)呢?因為每個數只可能被操作最多兩次,一次是加入隊列的時候,一次是因為有別的更大數在後面,所以被扔掉,或者因為出了窗口而被扔掉。
1 public class Solution { 2 public int[] maxSlidingWindow(int[] nums, int k) { 3 if(nums == null || nums.length == 0) return new int[0]; 4 LinkedList<Integer> deque = new LinkedList<Integer>(); 5 int[] res = new int[nums.length + 1 - k]; 6 for(int i = 0; i < nums.length; i++){ 7 // 每當新數進來時,如果發現隊列頭部的數的下標,是窗口最左邊數的下標,則扔掉 8 if(!deque.isEmpty() && deque.peekFirst() == i - k) deque.poll(); 9 // 把隊列尾部所有比新數小的都扔掉,保證隊列是降序的 10 while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) deque.removeLast(); 11 // 加入新數 12 deque.offerLast(i); 13 // 隊列頭部就是該窗口內第一大的 14 if((i + 1) >= k) res[i + 1 - k] = nums[deque.peek()]; 15 } 16 return res; 17 } 18 }
Leetcode 239.滑動窗口最大值