题目分析
题目分析
针对一个数组求符合长度长度最小的连续子数组。在数组或者字符串中根据约束条件求满足条件的连续数组,很明显可以通过滑动窗口的方法来解决。
解法一:滑动窗口
滑动窗口的代码如下,基本的滑动窗口套路
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int right = 0;
int curSum = 0;
int minLen = Integer.MAX_VALUE;
while(right < nums.length){
curSum += nums[right];
while (curSum >= target){
minLen = Math.min(minLen, right - left + 1);
curSum -= nums[left];
left++;
}
right++;
}
return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
时间复杂度:O(n),其中n是数组的长度,right和left最多移动n次。 空间复杂度:O(1)
解法二:前缀和 + 滑动窗口
题目还有个进阶,即是否可以尝试在时间复杂度O(nlog(n))解决,看到时间复杂度设计到log(n),题目又涉及到数组,那么肯定是用二分查找,但是二分查找的数组必须有序,原来的数组无序,怎么用二分查找呢?分析题目,发现题目是求和,那能不能用前缀和呢?题目的约束条件是数组中的每个数字都是正数,因此,数组的前缀和数组一定是有序的。因此可以利用前缀和+二分查找来求解。参考leetcode官方题解代码,代码示例如下:
public int minSubArrayLen(int target, int[] nums) {
int length = nums.length;
int[] sum = new int[length + 1];
int ans = Integer.MAX_VALUE;
for(int i = 1; i <= length; ++i){
sum[i] = sum[i - 1] + nums[i - 1];
}
for(int i = 1; i <= length; ++i){
int sumValue = target + sum[i-1];
int bound = Arrays.binarySearch(sum, sumValue);
if(bound < 0){
bound = -bound - 1;
}
if(bound <= length){
ans = Math.min(ans, bound - (i - 1));
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
虽然这种解法相比滑动窗口的解法复杂度增加了,但是为我们提供了一种新的思路,即看到log(n),联想到二分,联想到有序,进而联想到前缀和,最终可以写出上述解法。
Java中Arrays.binarySearch()用法
上面解法二用到了Arrays.binarySearch()这个api,之前只知道是在数组中对于目标数字进行二分查找,并且只在目标值存在的情况下使用,今天在理解解法二的时候,发现在目标值不存在的时候,返回值很有趣,之前没有记录过,特意记录下:
- 如果该值存在 则直接返回下标索引
- 如果该值不存在,返回第一个比目标值大的位置的索引值(但是此时,索引值的计算从0开始)的相反数。
简单代码测试如下:
int[] info = {1,2,3,4,5,8,9};
System.out.println(Arrays.binarySearch(info, 3));
System.out.println(Arrays.binarySearch(info, 7));
System.out.println(Arrays.binarySearch(info, 10));
总结
如有错误,恳请大家批评指正,日拱一卒,功不唐捐。
|