感谢力扣!
给定一个含有
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))
时间复杂度的解法。
1.分析题目
输入是一个正整数和一个正整数数组,找出该数组中长度最小的,总和大于等于target的连续子数组, 返回其长度。感觉这个也可以用前缀和解诶,思路的话因为是要大于等于target,又全部都是正数,所以n(假设找到的数组的数据个数)从大到小遍历,所以如果长的n的最大值小于target就不需要继续找了,下面肯定找不到。然后找的时候是可以用三层循环来做,也可以用前缀和来做,思路清晰的话,计划如下:
① 编写代码-三重循环并测试验证;
② 编写代码-前缀和并测试验证;
③ 看题解;
④ 测试验证;
⑤ 总结。
2.三重循环
思路就是遍历求子数组处理,但是n^3的时间复杂度,虽然做了一下过滤,但是理论上还是有这么大。
public int minSubArrayLen(int target, int[] nums) {
int result = 0;
for (int n = nums.length-1; n >=0; n--) {
int max = -1;
for(int i=0;i+n<nums.length;i++){
int m=i+n;
int sum=0;
for (int j = i; j <= m; j++) {
sum += nums[j];
}
max = Math.max(sum, max);
if (sum >= target) {
result = n+1;
}
}
if (max < target) {
break;
}
}
return result;
}
显而易见的还是没有过
没过也不优化了,直接前缀和试一下;
3.前缀和解
和昨天的思路一样,先求出前缀和,然后用双重循环遍历得到解。
public int minSubArrayLen(int target, int[] nums) {
int result = nums.length + 1;
int[] preSum = new int[nums.length];
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
preSum[i] = sum;
if(sum>=target && (i+1)<result){
result = i+1;
}
}
for (int i = 0; i < preSum.length; i++) {
for (int j = i + 1; j < preSum.length; j++) {
int temp = preSum[j] - preSum[i];
if(temp >=target && (j-i)<result){
result = j - i;
}
}
}
result = result == nums.length + 1 ? 0 : result;
return result;
}
然后,竟然还是超时,我的天!
4.学习题解
题解用的暴力法比我少一次循环,我思维太死板了,硬是把两次循环可以搞定的事情给搞成了三次循环,其实双重循环遍历过去所有的和也都求到了;然后前缀和呢也只写了一半,还需要配合二分查找,JAVA的话是Arrays.binarySearch,可以返回大于等于某个数的第一个位置;最后是滑动窗口,要用start和end两个变量存下标,然后通过start的有条件移动+end的有条件移动来得到题解,在当前这道题目上:
对start~end的所有元素求和sum
end移动条件:当前sum<target时移动end
start移动条件:当前sum>=target时,保存当前长度end-start+1,移动start
根据思路提示编写代码如下所示:
public int minSubArrayLen(int target, int[] nums) {
int start = 0, end = 0, sum = 0;
int result = nums.length+1;
while(end<nums.length){
sum += nums[end];
while (sum >= target) {
result = Math.min(end - start + 1,result);
sum -= nums[start];
start++;
}
end++;
}
result = result == nums.length + 1 ? 0 : result;
return result;
}
写的过程中有个点没有注意到, result要求最小的,忘记加Math.min了,导致求出来的结果不对。
5.简单总结
今天这个题目花的时间比较多,用了几种方法尝试发现自己考虑得都不够周全,很多代码的细节没有注意到,吭哧吭哧就在那里写,感觉接下来的时候还是要沉着一点,思考的过程中多问一下没有别的办法了嘛?但是也要考虑时间周期,没有什么事情是尽善尽美的,同时如果前人已经有答案了,要虚心学习和思考,内化成自己的东西,这么有什么丢脸的,并没有证明自己很笨,人类就是这么发展起来的,更别说个人了,加油!
① 算法不是只能用一个的,要明白算法优化的核心问题是什么,然后在解题的过程中去根据需求选算法;
② 一般题目可以思考的边界条件:算法全都不满足怎么办?所有判断全都是最坏情况怎么办?负数怎么办?下标操作有可能越界吗?常总结,常考虑。