数组-209长度最小的子数组-中等-20210906
1. 题目描述
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
输入:target = 4, nums = [1,4,4]
输出:1
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
1 <= target <= 10^9
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
2. 题目解答
2.1 暴力解法
思路:两个for循环
第二个for循环,不断加和,直到大于目标值;此时返回子序列长度j-i+1
,和之前的值比较并保留小的
时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if not nums: return 0 # 先判断数组是否为空
n = len(nums)
sub = n + 1 # 定义一个最大值
for i in range(n):
total = 0
for j in range(i, n):
total += nums[j]
if total >= target:
sub = min(sub, j - i + 1)
break
return 0 if sub == n + 1 else sub
#return sub if sub < n + 1 else 0
2.2 双指针/滑动窗口
根据当前子序列和大小的情况,不断的调节子序列的起始位置和终止位置,从而得出要想的结果
时间复杂度:O(n)
每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被被操作两次,所以时间复杂度是2 * n 【其中 n 是数组的长度。指针 start 和 end 最多各移动 n次。】
空间复杂度:O(1)
- 窗口:满足其和 ≥ s 的长度最小的 连续 子数组
- 窗口的起始位置:当前窗口的值大于s,窗口向前移动
- 窗口的结束位置:遍历数组的指针
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if not nums: return 0
n = len(nums)
sub = n + 1 # 定义一个最大值 无限大数float("inf")
i, total = 0, 0 # i滑动窗口起始位置
for j in range(n):
total += nums[j]
while total >= target:
sub = min(sub, j - i + 1)
total -= nums[i]
i += 1
return 0 if sub == n + 1 else sub
2.3 前缀和+二分查找
进阶:
- 如果你已经实现
O(n)
时间复杂度的解法, 请尝试设计一个O(n log(n))
时间复杂度的解法。
因为这道题保证了数组中每个元素都为正,所以前缀和一定是递增的,这一点保证了二分的正确性。如果题目没有说明数组中每个元素都为正,这里就不能使用二分来查找这个位置了。
思路:方法一的时间复杂度是 O ( n 2 ) O(n^2) O(n2),因为在确定每个子数组的开始下标后,找到长度最小的子数组需要$ O(n) 的 时 间 。 如 果 使 用 二 分 查 找 , 则 可 以 将 时 间 优 化 到 的时间。如果使用二分查找,则可以将时间优化到 的时间。如果使用二分查找,则可以将时间优化到O(logn)$。
- 额外创建一个数组$ \text{sums} 用 于 存 储 数 组 用于存储数组 用于存储数组 \text{nums}$ 的前缀和,其中 sums [ i ] \text{sums}[i] sums[i]表示从$ \text{nums}[0] 到 到 到 \text{nums}[i-1] $的元素和
- 得到前缀和之后,对于每个开始下标$ i$,可通过二分查找得到大于或等于 i i i 的最小下标$ \textit{bound}$,使得 sums [ bound ] − sums [ i − 1 ] ≥ s \text{sums}[\textit{bound}]-\text{sums}[i-1] \ge s sums[bound]−sums[i−1]≥s
- 更新子数组的最小长度(此时子数组的长度是$ \textit{bound}-(i-1)$)。
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
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
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n ) O(n) O(n)
3. 错解