开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情
今天来刷的题依旧是关于数组的。
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
暴力解法
先暴力解一遍,没啥技巧,很显然时间复杂度是O(n^2),设置了双层for循环,外层用于遍历数组寻找起始元素,内层用于计算数组元素和。同样的,这种解法在力扣是不通过的,因为超出时间限制了。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int cnt=99999;
for(int i=0;i<nums.length;i++){
int sum=0;
int cnt1=0;
for(int j=i;j<nums.length;j++){
sum+=nums[j];
if(sum>=target){
cnt1=j-i+1;
cnt=cnt1<cnt ? cnt1:cnt;
break;
}
}
}
return cnt==99999? 0:cnt;
}
}
滑动窗口
简单来说就还是双指针; 我们将j放在for循环中,作为窗口的右边界,遍历数组;而i作为窗口的左边界,当数组之和大于等于目标值时,才让i加一,这时要记住,要把sum也减去nums[i],因为他已经不包含在我们的窗口中了。
又学到一种写法,关于int的最大值
int cnt = Integer.MAX_VALUE;
滑动窗口的精妙之处就在于根据当前子序列和大小的情况,能够不断调节子序列的起始位置。从而将时间复杂度为O(n^2)的暴力解法降为O(n)。
代码如下
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int cnt=Integer.MAX_VALUE;
int i=0,sum=0,cnt1=0;
for(int j=0;j<nums.length;j++){
sum+=nums[j];
while(sum>=target){
cnt1=j-i+1;
cnt= cnt1<cnt? cnt1:cnt;
sum-=nums[i];
i++;
}
}
return cnt==Integer.MAX_VALUE? 0:cnt;
}
}
滑动窗口的难点主要就在于如何移动窗口的起始位置和结束位置,这里我们只用一个for循环就巧妙解决了,主要就是起始位置的移动变换了方式。
还有一点就是关于代码中为什么用while(sum>=target)而不是用if(sum>=target),这是因为我们将窗口起始位置往前移动一位后,可能总和依旧大于目标值,就需要我们继续把i往前移动,这样只用if是做不到的,if只能移动一次,而用while则可以一直移动到满足条件为止。
以上就是这道题需要注意的点,这道题不是很难,重点就是滑动窗口的思路。