博客昵称:沈小农学编程
作者简介:一名在读硕士,定期更新相关算法面试题,欢迎关注小弟!
PS:哈喽!各位CSDN的uu们,我是你的小弟沈小农,希望我的文章能帮助到你。欢迎大家在评论区唠嗑指正,觉得好的话别忘了一键三连哦!😘
题目难度:中等
默认优化目标:最小化时间复杂度。
Python默认为Python3。
目录
1 题目描述
2 题目解析
3 算法原理及代码实现
3.1 暴力求解
3.2 前缀和+二分查找
3.3 滑动窗口
参考文献
1 题目描述
给定一个含有 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 <= 109
-
1 <= nums.length <= 105
-
1 <= nums[i] <= 105
进阶:
-
如果你已经实现
O(n)
时间复杂度的解法, 请尝试设计一个O(n log(n))
时间复杂度的解法。
2 题目解析
输入是一个正整数数组nums
和一个正整数target
,输出是最短子数组长度。约束条件是子数组中的所有元素之和等于target
。
3 算法原理及代码实现
3.1 暴力求解
枚举每个下标作为子数组的开始,对于每个开始下标i,需要找到大于或等于i的最小下标j,使得nums[i]到nums[j]的元素大于或等于target,并更新子数组的长度。
时间复杂度O(n^2),空间复杂度O(1)。
C++代码实现
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
int sublen=10000;
if(!n) return 0;
else{
for(int i=0;i<n;i++){
int sum=0;
for(int j=i;j<n;j++){
sum+=nums[j];
if(sum>=target){
sublen=min(sublen,j-i+1);
break;
}
}
}
}
return sublen==10000 ? 0 : sublen;
}
};
Python代码实现
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n,sublen=len(nums),10000
if(not n):
return 0
else:
for i in range(n):
sum=0
for j in range(i,n):
sum+=nums[j]
if(sum>=target):
sublen=min(sublen,j-i+1)
break
return 0 if sublen==10000 else sublen
3.2 前缀和+二分查找
如果使用二分查找,可以将时间复杂度优化到O(log n)。为了使用二分查找,我们需要一个数组sums来记录nums的前缀和。因为nums中每个元素都是正的,所以前缀和递增。
时间复杂度O(n log n),空间复杂度O(n)。
C++代码实现
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
int sublen=1000000;
if(!n) return 0;
vector<int> sums(n+1,0);
for(int i=1;i<n+1;i++)
sums[i]=sums[i-1]+nums[i-1];
for(int i=1;i<n+1;i++){
int target_new=target+sums[i-1];
auto bound=lower_bound(sums.begin(),sums.end(),target_new);
if(bound!=sums.end())
sublen=min(sublen,static_cast<int>((bound-sums.begin()-(i-1))));
}
return sublen==1000000 ? 0 : sublen;
}
};
Python代码实现
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n,sublen=len(nums),1000000
if(not n):
return 0;
sums=[0]
for i in range(n):
sums.append(sums[-1]+nums[i])
for i in range(1,n+1):
target_new=target+sums[i-1]
bound=bisect.bisect_left(sums,target_new)
if bound!=len(sums):
sublen=min(sublen,bound-i+1)
return 0 if sublen==1000000 else sublen
3.3 滑动窗口
定义两个指针left和right表示滑动窗口的开始和结束位置,sum用于计算子数组中的元素和。
left和right初始都指向下标0。每一轮迭代,sum+=nums[end],如果sum大于等于target,更新sublen,然后sum减去sum[left],left右移。right右移。以此类推。
时间复杂度O(n),空间复杂度O(1)。
C++代码实现
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
int left=0,right=0;
int sublen=1000000;
int sum=0;
if(!n) return 0;
while(right<n){
sum+=nums[right];
while(sum>=target){
sublen=min(sublen,right-left+1);
sum-=nums[left];
left++;
}
right++;
}
return sublen==1000000 ? 0 : sublen;
}
};
Python代码实现
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n,sublen=len(nums),1000000
left,right,sum=0,0,0
if(not n):
return 0
while right<n:
sum+=nums[right]
while sum>=target:
sublen=min(sublen,right-left+1)
sum-=nums[left]
left+=1
right+=1
return 0 if sublen==1000000 else sublen
参考文献
力扣面试经典150题
力扣官方题解