Leetcode: Sliding Window Maximum
題目
給一組 array 數字,指定 sliding window 的 size 是 k ,每一次都只能看見 k 個數字,求每一次移動 window 的最大值
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3 Output: [3,3,5,5,6,7] Explanation: 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
思路
使用一個 queue 去紀錄 window 範圍裡的最大值, queue 裡放置元素由左至右是大到小,
▼ 每一次拿 queue.last 去跟 nums[right] 比,為什麼是 queue的 last element (下圖的右邊進入) 呢?
因為我們希望元素是由大到小是由左到右排列,所以先從小的開始比,當新元素大於原本 queue.last 的話,就把 queue.last 元素 pop 出去,如下圖 6 > 1 ,所以 1 向右移開
▼ window 開始了第一次向右 shift,這邊要把第一次的答案 output 出,也因為有了 queue 的隨時紀錄,所以每次 output 出答案,只要找 queue.first 就是最大值也就是答案,如下圖就是 6
▼ 下方有一個特殊的操作,當 window 向右,而原本最大的元素已經不在 window 範圍內時,需要從左把邊元素 queue.first 移出 queue ,如下圖就是 6
▼ 最後 queue 裡剩下 [5, 3] ,所以最後一個 window 的最大值,就是取 queue.first 即 5
這 test case 最後答案是 [6, 6, 5, 5]
Monotonic Queue 單調隊列
單調隊列 Monotonic queue 不屬於嚴格的 queue ,因為 queue 是 FIFO,一律從隊尾入,隊首出,
但 單調隊列 Monotonic queue 是從隊尾入,但可能從「隊首 or 隊尾出」,
而實作這種資料結構可以用 Deque 唸作 “deck”
程式碼
關鍵判斷:每次都會比較 deque 裡的 last ,如果 last 比現在的 nums[r] 大時,就 pop 出原本在 deque 的元素,
為什麼 deque 裡是放 index 不是 value?
注意 deque 裡面是放 nums 的 index,因為要判斷 deque 的 first 如果超出 left,就要把 deque.first 從左邊踢掉
class Solution {
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
var result = [Int]()
var deque = [Int]()
var l = 0
var r = 0
while r < nums.count {
// pop smaller values from deque
while !deque.isEmpty && nums[deque.last!] < nums[r] {
let last = deque.popLast()
}
deque.append(r)
if l > deque[0] {
deque.removeFirst()
}
if (r + 1) >= k {
result.append(nums[deque[0]])
l += 1
}
r += 1
}
return result
}
}
時間複雜度: O(n)