LeetCode-滑動窗口類題彙總

模板

  • 初始左指針(left)和右指針(right)都指向0
  • 右指針不斷向右移動
  • 當滿足窗口大小時候,進行相應的處理,這個和過程中需要left向右移動
def sliding_window(window_size, array):
    left, right = 0, 0
    res = 0
    while right < len(array):
        if condition:
            pass
        while n > window_size or n == window_size:
            ...
            left += 1 # 右移left
            res = max(res, xxx)
            ...
        res = max(res, xxx)
        right += 1    # 右移right

    return res

1、無重複字符的最長子串

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/    

此題不限制窗口大小,窗口不含有重複字符就可以

def lengthOfLongestSubstring(self, s):
        left, right = -1, 0  # left = -1 是爲了第一個字符計算方便
        maps = {}
        max_len = 0
        while right < len(s):
            # 初始或者當前字符在窗口左側出現過 
            if maps.get(s[right]) is None or maps.get(s[right]) < left:
                max_len = max(max_len, right-left)
            else:
                left = maps.get(s[right])
            maps[s[right]] = right
            right += 1

        return max_len

2、替換後的最長重複字符

給你一個僅由大寫英文字母組成的字符串,你可以將任意位置上的字符替換成另外的字符,總共可最多替換 k 次。在執行上述操作後,找到包含重複字母的最長子串的長度。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/longest-repeating-character-replacement/

此題不限制窗口大小,窗口能替換成一樣的字符就可以

def characterReplacement(self, s, k):
        if not s:
            return 0
        letter_num = {}
        left, right = 0, 0
        res = 0
        while right < len(s): 
            letter_num[s[right]] = letter_num.get(s[right], 0) + 1 # 統計字符個數
            max_letter = max(letter_num, key=letter_num.get)  # 計算字符數量最多的字符
            while right - left + 1 - letter_num[max_letter] > k: # 當替換次數大於閾值k,進入循環不斷將窗口左端字符移除,直到滿足要求
                letter_num[s[left]] -= 1
                left += 1
                max_letter = max(letter_num, key=letter_num.get)  # 此時要不斷記錄最大的窗口
            res = max(res, right - left + 1)
            right += 1

        return res

3、滑動窗口中位數

給出一個數組 nums,有一個大小爲 k 的窗口從最左端滑動到最右端。窗口中有 k 個數,每次窗口向右移動 1 位。你的任務是找出每次窗口移動後得到的新窗口中元素的中位數,並輸出由它們組成的數組。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/sliding-window-median/

排序數組的插入和查找用了個模塊bisect ,用法參考:https://www.cnblogs.com/skydesign/archive/2011/09/02/2163592.html

    def medianSlidingWindow(self, nums, k):
        result = []
        if not nums or not k:
            return result
        # 計算中位數
        def calculate_median(window, k):
            if k % 2 == 1:
                median = window[k//2]
            else:
                median = (window[k//2 - 1] + window[k//2])/2.0
            return median

        n = len(nums)
        left, right = 0, k
        # 計算第一個窗口的中位數
        window = sorted(nums[left:right])
        result.append(calculate_median(window, k))
        while right < n:
            bisect.insort(window, nums[right]) # 將當前數插入到窗口中
            # 刪除最左邊的數
            index = bisect.bisect_left(window, nums[left])
            del window[index]
            result.append(calculate_median(window, k)) # 重新計算中位數並記錄
            left += 1
            right += 1

        return result

4、字符串的排列

給定兩個字符串 s1 和 s2,寫一個函數來判斷 s2 是否包含 s1 的排列。

換句話說,第一個字符串的排列之一是第二個字符串的子串。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/permutation-in-string/

    def checkInclusion(self, s1, s2):
        if s1 in s2:
            return True
        if len(s1)>len(s2):
            return False
        counter = collections.Counter(s1)  # 統計s1字符
        left, right = 0, 0
        temp_counter = dict(counter)
        while right < len(s2):
            if s2[right] not in temp_counter:  # 若s1中不包含,則重新統計,恢復counter
                right += 1
                left = right
                temp_counter = dict(counter)
                continue
            else:
                while temp_counter[s2[right]] == 0: # 窗口中right字符已經夠數了,不斷移除左字符,直到能放得下右字符
                    temp_counter[s2[left]] += 1
                    left += 1
                temp_counter[s2[right]] -= 1
                if temp_counter[s2[right]] > 0: # 窗口中字符數量不滿足s1,繼續移動,,此模塊爲了提高效率(針對下文的all)
                    right += 1
                    continue
            if all(v == 0 for v in temp_counter.values()):  # 滿足則返回退出
                return True
            right += 1

        return False

5、K 個不同整數的子數組

給定一個正整數數組 A,如果 A 的某個子數組中不同整數的個數恰好爲 K,則稱 A 的這個連續、不一定獨立的子數組爲好子數組。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/subarrays-with-k-different-integers/

重複元素的數量要算在內

 def subarraysWithKDistinct(self, A, K):
        if not A or K == 0:
            return 0
        maps = collections.defaultdict(int)
        n = len(A)
        left, res, cnt, tmp = 0, 0, 0, 1
        for right in range(n):
            maps[A[right]] += 1
            if maps[A[right]] == 1: # 說明新來了一個字符,窗口不同字符數量+1
                cnt += 1   
            while cnt > K: # 不滿足要求,移動左指針
                tmp = 1   
                cnt -= 1
                maps[A[left]] -= 1
                left += 1
            # 移動左指針的條件,直到滿足條件,這裏需要注意的是左指針移動一次,算新來一個數!這個時候才tmp++
            while maps[A[left]] > 1: # 大於1說明有重複元素,留下一個就可以,tmp記錄有重複字符時候的子數組數量
                tmp += 1
                maps[A[left]] -= 1
                left += 1
            if cnt == K:
                res += tmp
        return res

6、滑動窗口最大值

給定一個數組 nums,有一個大小爲 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字。滑動窗口每次只向右移動一位。

返回滑動窗口中的最大值。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/sliding-window-maximum/

應用單調雙邊隊列(遞減),保證最大值在第一個,實現雙邊隊列用 from collections import deque

    def maxSlidingWindow(self, nums, k):
        # 單調雙邊隊列
        if not nums or not k :
            return []
        # push進新元素,滿足單調性
        def push(queue, nums, i):
            while queue and nums[i] > nums[queue[-1]]:
                queue.pop()
            queue.append(i)
            
        from collections import deque
        res = []
        queue = deque()
        for i in range(k-1):  # 將比窗口小1個初始元素放進隊列
            push(queue, nums, i)
        
        for i in range(k-1, len(nums)):
            push(queue, nums, i)   # 不斷放進新元素
            res.append(nums[queue[0]])  # 讀最大值保存
            if k == i-queue[0]+1: # 窗口裝滿了,移除最左的
                queue.popleft()
        
        return res

7、最長湍流子數組

當 A 的子數組 A[i], A[i+1], ..., A[j] 滿足下列條件時,我們稱其爲湍流子數組:

若 i <= k < j,當 k 爲奇數時, A[k] > A[k+1],且當 k 爲偶數時,A[k] < A[k+1];
或 若 i <= k < j,當 k 爲偶數時,A[k] > A[k+1] ,且當 k 爲奇數時, A[k] < A[k+1]。
也就是說,如果比較符號在子數組中的每個相鄰元素對之間翻轉,則該子數組是湍流子數組。

返回 A 的最大湍流子數組的長度。
鏈接:https://leetcode-cn.com/problems/longest-turbulent-subarray/

def maxTurbulenceSize(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        if len(A) < 2:
            return len(A)
        max_len = 1
        left, right = 0, 2
        while right < len(A):
            if A[right - 1] == A[right]:
                left = right
                right += 1
            elif A[right - 2] > A[right - 1] < A[right] or A[right - 2] < A[right - 1] > A[right]:
                max_len = max(max_len, right - left + 1)
                right += 1
            elif A[right - 1] < A[right] or A[right - 1] > A[right]:
                max_len = max(max_len, 2)
                left = right - 1
                right += 1
        return max_len

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章