题目
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 **target ** 的长度最小的 子数组
[numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度 。*如果不存在符合条件的子数组,返回 0 。
示例 1:
输入: target = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入: target = 4, nums = [1,4,4]
输出: 1
示例 3:
输入: target = 11, nums = [1,1,1,1,1,1,1,1]
输出: 0
提示:
1 <= target <= 1091 <= nums.length <= 1051 <= nums[i] <= 105
思路
解法一:滑动窗口(自)
滑动窗口经典题型,left、right指针初始化0,然后外框架right指针一直移动<n,内部第一个满足sum >= target的情况,然后一直移动left指针,计算r-l+1,取最小值更新答案,直到sum < target,进入下一个right。
时间复杂度O(N),空间复杂度O(1)
代码一: 滑动窗口
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 双指针
left = 0
right = 0
n = len(nums)
res = n + 1
s = 0 # 记录滑动窗口的和
while right < n:
s += nums[right]
if s < target:
right += 1
continue
while s >= target:
res = min(res, right-left+1)
s -= nums[left]
left += 1
right += 1
return res if res != n + 1 else 0
解法2: 前缀和+二分查找定位
解法2也就是官解2,利用题意的所有值都大于0,可以发现数组的区间和一定是单调递增的,可以通过构造前缀和sums数组,sums数组长度是 len(nums)+1,其中 sums[i] = Sum(nums[0],...nums[i-1])。
如何找到target?
通过在sums中查找target,利用Sum(nums[i],...,nums[j]) = sums(j-1) - sums(i) >= target即可。
- 遍历sums,从1开始,往右查找sum(j),使其满足 sums(j) - sums(i) >= target,由于sums是单调递增的,所以从i位置开始可以使用二分查找定位到满足条件的sums的j下标
- 在找到满足条件的最左j坐标后,更新结果,res = min(res, j-i+1)
时间复杂度O(Nlog(N)),空间复杂度O(N)
代码二:前缀和+二分定位
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 二分查找
def get_lowest_bound(arr, l, r, target):
while l < r:
mid = (l + r) >> 1
if arr[mid] < target:
l = mid + 1
else:
r = mid
return l if arr[l] >= target else -1
# 前缀和+二分查找
n = len(nums)
res = n + 1
# 构造前缀和列表
sums = [0]
for i in nums:
sums.append(sums[-1] + i)
for i in range(1, n+1):
bound = get_lowest_bound(sums, i, n, sums[i-1]+target)
if bound != -1:
res = min(res, bound- i+1)
return res if res != n + 1 else 0