單調佇列

九年义务漏网鲨鱼發表於2024-10-27

基本內容

一種維護陣列視窗最大值或者最小值的方法

  • 入門例子 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也不可能是最大值。
  • 其他注意事項
  • 用陣列儲存視窗的效率不如雙端佇列,因為有刪除第一個元素的操作;

  • 視窗記憶體儲索引的效率往往會比儲存元素值的效率要高,不同儲存方式判斷視窗是否滿了的方式不同;

題目

  1. Leetcode 918. 環形子陣列的最大和

    該題目前採用動態規劃的變形解決

  2. 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
    
  3. 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]
    

相關文章