基本內容
一種維護陣列視窗最大值或者最小值的方法
- 入門例子 Leetcode 239. 滑動視窗最大值
題目要求:依次輸出在陣列中以k為大小的滑動視窗的最大值
- 基本思想
在視窗大小上限為k的陣列視窗中,始終保持該視窗的第一個值為該視窗的最大(小)值,並且視窗內的剩餘值單調遞減(遞增)
- 核心程式碼解釋
# 當i已經遍歷到5
array = [1,3,-1,-3,5,3,6,7] #原陣列
window = deque()
for i in range(len(array)):
if ... # 視窗是否已經滿了的判斷,如果滿了,彈出第一個視窗
window.popleft()
while window and window[-1] < array[1]: # 5大於視窗內剩餘元素,依次彈出,直到視窗為空
window.pop() #雙端佇列pop預設刪掉最後一個
array.append(array[i]) # 加入5,此時5是最大的
# i遍歷下一個元素3,視窗還未滿且小於視窗內最後一個元素5,直接加入,因為如果5被彈出的話,3有可能是剩餘視窗的最大值,所以需要保留了
# i遍歷下一個元素4,視窗還未滿但大於視窗內最後一個元素3,需要先把3彈出,因為即使5被彈出了,3也不可能是最大值。
- 其他注意事項
用陣列儲存視窗的效率不如雙端佇列,因為有刪除第一個元素的操作;
視窗記憶體儲索引的效率往往會比儲存元素值的效率要高,不同儲存方式判斷視窗是否滿了的方式不同;
題目
-
Leetcode 918. 環形子陣列的最大和
該題目前採用動態規劃的變形解決
-
Leetcode 1438. 絕對差不超過限制的最長連續子陣列
- 維護最大值和最小值,思路是對的,就差最後一步,將維護最大值和最小值的索引相減取最大值。
- 另一種解法:滑動視窗 + 有序集合 (平衡樹)
- 類似的題目:
def longestSubarray(self, nums: List[int], limit: int) -> int: windowmax = deque() windowmin = deque() Vmax = 0 left = 0 for i in range(0, len(nums)): while windowmax and nums[i] < nums[windowmax[-1]]: windowmax.pop() while windowmin and nums[i] > nums[windowmin[-1]]: windowmin.pop() windowmax.append(i) windowmin.append(i) while windowmax and windowmin and (nums[windowmin[0]] - nums[windowmax[0]]) > limit: if nums[left] == nums[windowmax[0]]: windowmax.popleft() if nums[left] == nums[windowmin[0]]: windowmin.popleft() left += 1 Vmax = max(Vmax, i - left + 1) return Vmax
-
Leetcode 3175. 找到連續贏 K 場比賽的第一位玩家
每日一題, 與單調佇列相似,也可以看作是維護一個視窗的最大值
def findWinningPlayer(self, skills: List[int], k: int) -> int: window = deque() window.append(0) cnt = 0 for i in range(1,len(skills)): if window and skills[window[0]] < skills[i]: window.pop() window.append(i) cnt = 1 if cnt ==k : return window[0] else: cnt += 1 if cnt == k: return window[0] return window[0]