[leetcode 209]長度最小的子數組(Python)

題目描述

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

示例:

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

進階:

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

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/minimum-size-subarray-sum


結題思路

思路一:暴力遍歷:(Python超時)

初始化子數組的最小長度爲無窮大,枚舉數組 nums 中的每個下標作爲子數組的開始下標,對於每個開始下標 i,需要找到大於或等於 i 的最小下標 j,使得從 nums[i]nums[j] 的元素和大於或等於 s,並更新子數組的最小長度(此時子數組的長度是 j-i+1)。

- 複雜度分析

  1. 時間複雜度:O(n2)O(n^2)nn爲數組長度。
  2. 空間複雜度:O(1)O(1)

思路二:二分查找+前綴和

暴力法中再確定每個子數組的開始下標後,找到長度最小的子數組需要O(n)O(n)的時間。如果使用二分查找,可以將時間複雜度降到O(logn)O(logn)
使用二分查找要額外創建一個數組用來存儲數組numsnums的前綴和,其中sums[i]sums[i]表示從nums[0]nums[0]nums[i1]nums[i-1]的元素和。得到前綴和之後對於每個開始下標i,可以通過二分查找得到>=i的最小下標boundbound,使得sums[bound]sums[i1]ssums[bound]-sums[i-1] ≥ s,並更新子數組的最小長度。
因爲題目中的數組元素均爲正數,所以前綴和一定是遞增的,這是使用二分查找的正確性的前提。Pythonbisect.bisect_left實現了二分查找大於等於某個數的第一個位置的功能。

- 複雜度分析

  1. 時間複雜度:O(nlogn)O(nlogn)nn爲數組長度。
  2. 空間複雜度:O(n)O(n),額外創建數組sums存儲前綴和。

思路三:雙指針

前兩種方法都是每次確定子數組的開始下標後得到長度最小的子數組,爲了降低時間複雜度可以使用雙指針。
定義指針 startstartendend 分別表示子數組的開始位置和階數位置,維護變量 sumsum 存儲子數組中的元素和。
開始時 startstartendend 都指向下標0,sum=0sum=0
每一輪迭代都將 nums[end]nums[end] 加到 sumsum 中,如果 sumssum≥s ,更新子數組的最小常數,然後從 sumsum 中減掉 nums[start]nums[start] ,並把 startstart 右移,直到 sum<ssum<s ,此過程中要更新子數組最小長度。每輪迭代後將 endend 後移。

- 複雜度分析

  1. 時間複雜度:O(n)O(n),其中nn是數組的長度。指針 startstartendend 最多各移動 nn 次。
  2. 空間複雜度:O(1)O(1)

代碼實現

思路二:二分查找+前綴和

class Solution:
    def minSubArrayLen(self, s, nums):
        if not nums:
            return 0
        
        n = len(nums)
        ans = n + 1
        sums = [0]
        for i in range(n):
            sums.append(sums[-1] + nums[i])
        
        for i in range(1, n + 1):
            target = s + sums[i - 1]
            bound = bisect.bisect_left(sums, target)
            if bound != len(sums):
                ans = min(ans, bound - (i - 1))
        
        return 0 if ans == n + 1 else ans

#作者:LeetCode-Solution
#鏈接:https://leetcode-cn.com/problems/minimum-size-subarray-sum/solution/chang-du-zui-xiao-de-zi-shu-zu-by-leetcode-solutio/

思路三:雙指針

class Solution(object):
    def minSubArrayLen(self, s, nums):
        n = len(nums)
        start, end = 0,0
        mysum = 0
        ans = n+1
        while end < n:
            mysum += nums[end]
            while mysum >= s:
                ans = min(ans, end-start+1)
                mysum -= nums[start]
                start += 1
            end += 1

        return 0 if ans == n+1 else ans

Tips

  1. 涉及連續子數組的問題通常有兩種思路:一是滑動窗口、二是前綴和
  2. Pythonbisect.bisect_left實現了二分查找大於等於某個數的第一個位置的功能。

Author:ChierAuthor: Chier

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