程式碼隨想錄陣列二刷:長度最小的子陣列(滑動視窗)

Bathwind_W發表於2024-07-20

程式碼隨想錄陣列二刷:長度最小的子陣列(滑動視窗)

leetcode209

在這裡插入圖片描述
這道題採用滑動視窗的思想去做。
實現滑動視窗,主要確定如下三點:

  1. 視窗內是什麼?
  2. 如何移動視窗的起始位置?
  3. 如何移動視窗的結束位置?
    視窗就是 滿足其和 ≥ s 的長度最小的 連續 子陣列。視窗的起始位置如何移動:如果當前視窗的值大於等於s了,視窗就要向前移動了(也就是該縮小了)。視窗的結束位置如何移動:視窗的結束位置就是遍歷陣列的指標,也就是for迴圈裡的索引。
    具體程式碼如下:
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        l = len(nums)
        left,right = 0,0
        min_len = float('inf')
        cur_sum = 0
        while right < l:
            cur_sum += nums[right]
            while cur_sum >= target:
                min_len = min(min_len, right - left + 1)
                cur_sum -= nums[left]
                left += 1
            right += 1
        return min_len if min_len!=float('inf') else 0

接下來來具體分析這個程式碼,因為是滑動視窗,那麼這個視窗是怎麼構成的呢,是不是就是left和right組成的區間長度,那麼何時去計算這個長度,是不是區間裡面的和大於等於target時候開始去計算長度。也就是:while cur_sum >= target,然後左邊開始移動,移動的原因是因為這個和可能遠超你要求的target,所以可以移動左邊來縮小視窗。由於你左邊移動了,就要把個值從總體的和剔除掉。那麼啥時候退出這個內部迴圈呢。也就是求出的和小於target時候,就去移動右邊界。
下面來看水果成籃這道題:

leetcode904

在這裡插入圖片描述
這道題也是採用滑動視窗去解決問題。在做這道題時候,要明白幾個問題才能去做好這道題

  1. 視窗內是什麼?
  2. 如何移動視窗的起始位置?
  3. 如何移動視窗的結束位置?

這道題視窗內就是能摘的樹的數量。但是陣列裡面的數字相當於是標記一樣,這裡要用到雜湊表去記錄一下。因為最多兩個籃子,所以種類最多是兩種。當超過兩個籃子時候,左邊視窗開始移動,同時雜湊表關於該項的值要減去1,如果該項的值為0,則刪除這一項。
具體程式碼如下:

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        count = collections.defaultdict(int)
        left,max_len,right = 0,0,0
        while right < len(fruits):
            count[fruits[right]] += 1
            while len(count) > 2:
                count[fruits[left]] -= 1
                if count[fruits[left]] == 0:
                    del count[fruits[left]]
                left += 1
            max_len = max(max_len, right - left + 1)
            right += 1
        return max_len

程式碼步驟解析

  1. 初始化變數

    • countdefaultdict(int),用於記錄視窗內每種水果的數量。
    • leftright:視窗的左右邊界。
    • max_len:記錄遇到的最大視窗長度。
  2. 擴充套件視窗

    • 使用 right 指標遍歷 fruits 陣列,每次迴圈中將 fruits[right] 加入到視窗(即雜湊表 count 中增加對應水果的計數)。
  3. 調整視窗大小

    • 當雜湊表中的鍵(即水果型別)數量超過 2 時,開始移動 left 指標縮小視窗,直到視窗內的水果型別數不超過兩種。
    • 如果某種水果的數量減到 0,則從雜湊表中刪除該水果。
  4. 更新最大長度

    • 在每次迴圈的末尾,更新 max_len 為當前視窗的長度(right - left + 1)和已記錄的 max_len 中的較大值。
  5. 返回結果

    • 迴圈結束後,返回 max_len 作為最大可以摘的水果數量。

效能分析

  • 時間複雜度:O(N),其中 N 是 fruits 陣列的長度。儘管內部有一個 while 迴圈來調整視窗大小,但每個元素最多被加入和刪除一次。
  • 空間複雜度:O(1),雖然使用了雜湊表,但由於最多隻儲存兩種水果的計數,所以空間複雜度為常數。
    下面繼續來看一道題;

leetcode76

在這裡插入圖片描述
這道題也是用滑動視窗+雜湊表去做。首先s的長度小於t的長度是一定不滿足的,直接返回空字串即可。
外層while迴圈依舊控制視窗右邊界,內層迴圈控制左邊界。只不過這道題需要用到雜湊表記錄t對應字元個數。具體程式碼如下:

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        if len(s) < len(t):
            return ""
        hash_t = collections.defaultdict(int)
        hash_s = collections.defaultdict(int)
        min_len = float('inf')
        min_window = ""
        left,right,formed= 0,0,0
        for i in range(len(t)):
            hash_t[t[i]] += 1
        hash_t_lenth = len(hash_t)
        while right < len(s):
            hash_s[s[right]] += 1
            if s[right] in hash_t and hash_s[s[right]] == hash_t[s[right]]:
                formed += 1
            while  formed == hash_t_lenth:
                if right - left + 1 < min_len:
                    min_len = right - left + 1
                    min_window = s[left:right+1]
                hash_s[s[left]] -= 1
                if hash_s[s[left]] < hash_t[s[left]]:
                    formed -= 1
                left += 1
            right += 1
        return min_window

下面繼續看幾道滑動視窗相關的題目:

leetcode3

在這裡插入圖片描述
具體程式碼如下:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        left,right = 0,0
        maxlen = 0
        hashmap = collections.defaultdict(int)
        while right < len(s):
            hashmap[s[right]] += 1
            while hashmap[s[right]] > 1:
                hashmap[s[left]] -= 1
                if hashmap[s[left]] == 0:
                    del hashmap[s[left]]
                left += 1
            maxlen = max(maxlen, right - left + 1)
            right += 1
        return maxlen

相關文章