[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

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