LeetCode 209. 長度最小的子數組 | Python

209. 長度最小的子數組


題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/minimum-size-subarray-sumpython

題目


給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中知足其和 ≥ s 的長度最小的連續子數組,並返回其長度。若是不存在符合條件的連續子數組,返回 0。數組

示例:bash

輸入: s = 7, nums = [2,3,1,2,4,3]
輸出: 2
解釋: 子數組 [4,3] 是該條件下的長度最小的連續子數組。

進階:微信

若是你已經完成了O(n) 時間複雜度的解法, 請嘗試 O(n log n) 時間複雜度的解法。app

解題思路


思路:雙指針 / 前綴和+二分查找spa

先看題目,題目說明,數組 nums 給定的元素都是正整數,同時給一個整數 s,求子數組和大於等於 s 的最小連續子數組,返回長度。不存在則返回 0。3d

這裏先說下特殊狀況,上面根據題意已說明,nums 中的元素都是正整數,那麼若是數組全部元素和都小於 s 的話,那麼子數組是必定不能知足要求的,直接返回 0 便可。指針

進階 提示的內容部分中,要求先完成 O(n) 時間複雜度的解法。這就是咱們如今要說明的 雙指針 思路的解法。code

具體的實現思路:blog

  • 先定義雙指針 leftright,同時指向索引爲 0 的初始位置;
  • 定義 num_sum 存儲數組和,用以與 s 比較;
  • 先移動右指針,維護更新 num_sum 值,當 num_sum 知足大於等於 s 的條件時,先記錄此時的數組長度,而後嘗試縮小數組的長度。
  • 此時移動左指針,剔除左邊的元素,再次判斷 num_sums 的值。循環直至指針到達末尾位置。

雙指針實現思路的過程,可見下圖:

圖解

關於圖中的參數
當 num_sum 大於等於 s 時表示知足條件。
s:題目所給的目標值
num_sum:子數組之和
left:左指針
right:右指針
ans:知足條件的子數組長度

具體的代碼見 【代碼實現 # 雙指針】。

上面是雙指針的思路,下面嘗試使用 O(nlogn) 複雜度的方法:前綴和 + 二分查找。

關於前綴和,這裏就不展開說明這個概念了。這裏咱們用數組 num_sum 存儲 nums 的前綴和。

num_sum[i]:表示 nums[0]nums[i-1] 的元素之和。

仍是由於有 【給定一個含有 n 個正整數的數組】 的前提,那麼咱們存儲前綴和的數組必定是遞增升序的,這樣也能保證二分查找的正確性。

當咱們獲得存儲前綴和的數組以後,咱們能夠遍歷數組經過二分查找獲得一個索引 index,使得 num_sum[index] - num_sum[i-1] >= s,更新維護子數組的長度,(此時的長度爲 index -(i-1))

具體的代碼見 【代碼實現 # 前綴和+二分查找】。

代碼實現


# 雙指針
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        # 先處理特殊狀況,由於給定的數組都是正整數
        # 若是數組中全部元素的和都小於 s
        # 那麼子數組的和必定都小於 s,直接返回 0
        if sum(nums) < s:
            return 0

        # 定義雙指針
        left = 0
        right = 0

        length = len(nums)
        # 定義變量,存儲元素和,用以跟 s 比較
        num_sum = 0
        ans = float('inf')

        # 遍歷數組,移動指針
        while right < length:
            num_sum += nums[right]
            # 知足條件時,嘗試縮小子串的長度,尋找最小的連續子數組
            while num_sum >= s:
                ans = min(ans, right - left + 1)
                num_sum -= nums[left]
                left += 1
            right += 1
        
        return ans


# 前綴和+二分查找
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        def bin_search(num_sum, left, right, target):
            while left < right:
                mid = (left + right) // 2
                if num_sum[mid] < target:
                    left = mid+1
                else:
                    right = mid
            # 這裏要注意,有可能最後一位元素大於目標值
            # 這種狀況要考慮,若是不知足,也就是最後一位元素不大於目標值時,返回 -1,這種狀況不更新計算長度。
            return left if num_sum[left] >= target else -1
        
        # 先處理特殊狀況,當數組中的元素和小於 s,
        # 則表示數組中全部子數組的和都不可能大於 s
        # 這個時候,直接返回 0
        if sum(nums) < s:
            return 0
        
        ans = float('inf')

        length = len(nums)

        # 存儲前綴和
        num_sum = [0]
        for i in range(length):
            num_sum.append(num_sum[-1] + nums[i])
        # 經過二分查找,找到符合條件的索引,計算長度
        for i in range(1, length+1):
            target = s + num_sum[i-1]
            index = bin_search(num_sum, 1, length, target)
            if index != -1:
                ans = min(ans, index-(i-1))
        
        return ans

實現結果


雙指針 | 實現結果

實現結果 | 雙指針

前綴和+二分查找 | 實現結果

實現結果 | 前綴和+二分查找

總結


  • 題目中說明,給定的數組元素都是正整數,這是個一個大前提,可以有效的幫助解決問題。
  • 題目中要求先實現 O(n) 時間複雜度的解法。這裏使用雙指針的方法,具體實現以下:

    • 初始化雙指針 left, right,定義變量 num_sum 存儲子數組的和。
    • num_sum 小於目標值 s 時,先移動右指針,維護更新 num_sum。當 num_sum 知足條件也就是大於或等於 s 時,先記錄此時子數組的長度。此時移動左指針嘗試縮小數組,再次比較 num_sums 的值。循環判斷直至 right 到達末尾位置。
  • 題目進階說能夠嘗試 O(nlogn) 時間複雜度的解法,這裏可聯想到二分查找。一樣根據第一項所說的前提,能夠定義數組存儲 nums 數組的元素前綴和。具體實現以下:

    • 定義 num_sum 存儲數組的前綴和,num_sum[i] 表示 nums[0]nums[i-1] 元素之和。
    • 經過二分查找,能夠定位到一個索引 index 使得 num_sum[index]-nums[i-1] 的和大於或等於 s,記錄此時的子數組長度(index-(i-1))。

文章原創,若是以爲寫得好,歡迎關注點贊。微信公衆號《書所集錄》同步更新,一樣歡迎關注點贊。

qrcode_for_Demon

相關文章
相關標籤/搜索