大家好,我是河海哥,专注于后端,如果可以的话,想做一名code designer而不是普通的coder,一起见证河海哥的成长,您的评论与赞与关注是我的最大动力,如有错误还请不吝赐教,万分感谢。一起支持原创吧!纯手打有笔误还望谅解。
1-1:题目描述
Given an array of positive integers nums and a positive integer target, return the minimal length of a contiguous subarray [numsl, numsl+1, …, numsr-1, numsr] of which the sum is greater than or equal to target. If there is no such subarray, return 0 instead.
Example 1:
Input: target = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: The subarray [4,3] has the minimal length under the problem constraint.
Example 2:
Input: target = 4, nums = [1,4,4]
Output: 1
Example 3:
Input: target = 11, nums = [1,1,1,1,1,1,1,1]
Output: 0
Constraints:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
Follow up: If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log(n)).
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目理解:
在一个数组中间找到最小的数组,它的和=target就行。
1-2:滑动窗口解法
☘️算法流程:
核心思想是用一个可伸缩的滑动窗口去控制子数组的大小。整个窗口是左闭右闭型,[start, end],每次这种范围型的题目,一定要先自己想好自己的范围在哪?区间开闭? start控制窗口的头,end控制窗口的尾,每次start都往前面推一个,代表窗口在移动,end控制窗口的值的总和大小,一旦窗口内的和超过了target,end就结束,数组的长度end - start + 1。
☘️整体的思路并不难,难就难在对边界的掌握。我是没有能力一步就写到位的,只能通过debug去多次尝试。
public static int minSubArrayLen(int target, int[] nums) {
int n = nums.length;
int minLength = Integer.MAX_VALUE;
int start = 0;
int end = -1;
int sum = 0;
while (start < n) {
while (sum < target && end != n - 1) {
end++;
sum += nums[end];
}
if (sum >= target) {
minLength = Math.min(minLength, end - start + 1);
}
sum -= nums[start];
start++;
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
时间复杂度O(N)
空间复杂度O(1)
☘️答案的思路和我的似乎不太一样,它是维持一个sum和,每次都通过end往sum里添加数,当达到target的时候,就通过start对这个窗口进行瘦身,把它变成和刚好<target,只要再来几个数就能满足和大于target。不明白可以看一下官方解答的图,其实和上面的大同小异。代码如下:
public static int minSubArrayLen2(int target, int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int minLength = Integer.MAX_VALUE;
int start = 0;
int end = 0;
int sum = 0;
while (end < n) {
sum += nums[end];
while (sum >= target) {
minLength = Math.min(minLength, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
时间复杂度O(N)
空间复杂度O(1)
1-3:前缀和+二分查找
☘️我这里的和答案有一点区别,我这里前缀和是啥呢?prefix[i] = nums[0:i]之和,左闭区间,右闭区间。比如prefix[1] = nums[0] + nums[1];
☘️我们先给出一般情况下的示意图:
☘️由于nums数组中的值都是整数,所以前缀数组必然是递增的,这样就可以找到符合要求的bound,什么样的bound符合要求,在上面写了。所以整个算法有这样几个步骤:
- 填充prefix前缀和数组
- 从前缀和第一个位置0开始,二分查找符合要求的bound,并且计算数组区间长度加以更新。
public static int minSubArrayLen3(int target, int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int minLength = Integer.MAX_VALUE;
int[] prefix = new int[n]; // prefix[i] : nums[0:i] 之和
int sum = 0;
// fill prefix[]
for (int i = 0; i < n; i++) {
sum += nums[i];
prefix[i] = sum;
}
for (int i = 0; i < n; i++) {
int val = (i == 0 ? target : target + prefix[i - 1]);
int bound = Arrays.binarySearch(prefix, val); // bound = -insertion point - 1
// can't find the val
if (bound < 0) {
bound = -bound - 1;
}
// if find the val, the scope of bound is from 0 to n-1. [0:n-1]
if (bound <= n - 1) {
minLength = Math.min(minLength, bound - i + 1);
}
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
时间复杂度O(nlogn)
空间复杂度O(n)
☘️这里要特别注意java的二分查找,如果找到返回目标的index,如果找不到,就返回-该插入的位置的索引-1。为啥这样呢?比如0的时候,不-1就不是负号了,秒啊!所以<0就是没找到。然后再计算出符合要求的bound,这里的“插入的位置的索引”就是刚好前缀和>target的值。可以debug试试,你就知道我在说啥了。比如:
int[] nums = new int[]{2, 3, 1, 2, 4, 3};
int target = 7;