Title
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的连续子数组,返回 0。
**示例: **
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
双指针
Solve
定义两个指针start和end分别表示子数组的开始位置和结束位置,维护变量sums存储子数组中的元素和。
初始状态下,start=0,end=0,sums=0。
每一轮迭代,将num[end]加到sums,如果sums>=s,则更新子数组的最小长度(此时子数组的长度为end-start+1),然后将num[start]从sums中减去并将start右移,直到sums<s,在此过程中同样更新子数组的最小长度,在每一轮迭代的最后,将end右移。
Code
def minSubArrayLen_double_pointer(self, s: int, nums: List[int]) -> int:
if not nums:
return 0
start, end, sums, length, ans = 0, 0, 0, len(nums), len(nums) + 1
while end < length:
sums += nums[end]
while sums >= s:
ans = min(ans, end - start + 1)
sums -= nums[start]
start += 1
end += 1
return ans if ans != length + 1 else 0
复杂度分析
时间复杂度:O(n),其中 n 是数组的长度。指针 start 和 end 最多各移动 n 次。
空间复杂度:O(1)。
前缀和+二分查找
因为这道题保证了数组中每个元素都为正,所以前缀和一定是递增的,这一点保证了二分的正确性。如果题目没有说明数组中每个元素都为正,这里就不能使用二分来查找这个位置了。
额外创建一个数组sums用于存储数组nums的前缀和,其中sums[i]表示从nums[0]到num[i-1]的元素和,对于每个下标i,可通过二分查找得到大于或等于i的最小下标bound,使得sums[bound]-sums[i-1]>=s,并更新子数组的最小长度(此时子数组的长度为bound-i+1)。
Code
def minSubArrayLen_prefixSumAndBinarySearch(self, s: int, nums: List[int]) -> int:
if not nums:
return 0
sums, length, ans = [0], len(nums), len(nums) + 1
for i in range(length):
sums.append(sums[-1] + nums[i])
for i in range(1, length + 1):
target = s + sums[i - 1]
bound = bisect.bisect_left(sums, target)
if bound != len(sums):
ans = min(ans, bound - i + 1)
return ans if ans != length + 1 else 0
复杂度分析
时间复杂度:O(nlogn),其中 n 是数组的长度。需要遍历每个下标作为子数组的开始下标,遍历的时间复杂度是 O(n),对于每个开始下标,需要通过二分查找得到长度最小的子数组,二分查找得时间复杂度是 O(logn),因此总时间复杂度是 O(nlogn)。
空间复杂度:O(n),其中 n 是数组的长度。额外创建数组 sums 存储前缀和。