LeetCode209.长度最小的子数组(滑动窗口例题,后有练习题)
题目
思路
这道题的暴力做法应该是非常容易想到的,我们可以设置两重循环,一重循环作为子数组的起始位置,一重循环作为子数组的终止位置,然后计算子数组的大小是否大于等于target,如果是的话,就比较子数组长度。通过上述的方式就可以得出答案,时间复杂度毋庸置疑是 O ( n 2 ) O(n^2) O(n2)。
但是我们这道题的时间复杂度是可以优化到 O ( n ) O(n) O(n)的,使用的方法就是滑动窗口。
我们设置一个变量j,它的作用是遍历这个数组,也起到了子数组的终止位置的作用,然后我们再设置一个遍历i,它的作用是作为子数组的起始数组。
我们用变量j开始遍历数组,并且每次遍历到的数组的值都需要加到sum中,即sum+=数组的值,sum这个变量表示的就是子数组的总和。当我们遇到sum>=target的情况时,我们就满足了题目所要求的条件——子数组总和大于等于target,但是题目要求我们求出长度最小的子数组。所以我们考虑移动i,之所以可以移动i是因为我们不知道sum-nums[i]的值是否还是大于等于target的,如果还是大于等于target的话,那意味着我们即使移动了i,那么还是满足子数组总和大于等于target的,但是子数组的长度却变小了。
因此我们可以得到一个方法,即我们一开始让j向后移动,直到子数组的总和大于等于target,然后我们移动i,直到子数组的总和小于target(在子数组的总和将要小于target的时候,子数组的长度就是最小的)。而当子数组的总和小于target后,我们再重新开始移动j。我们可以发现,这个方法的移动轨迹像是一个不断变大变小的窗口,我想这也就是叫做滑动窗口的原因吧。
代码
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int ans=nums.length+1;
int sum=0;
int i=0;
for(int j=0;j<nums.length;j++)
{
sum+=nums[j];
while(sum>=target)
{
ans=Math.min(ans,j-i+1);
sum-=nums[i++];
}
}
if(ans>nums.length)
return 0;
else
return ans;
}
}
练习题
之所以在这里设置了一个练习题,是因为我在做这道题的时候,题目看的我有点懵,也想让别人感受一下。
题目(LeetCode904.水果成篮)
思路(代码部分有具体的思路)
我认为这道题目的难点在于读题,有两个篮子——这就等于例题中说的target,在这道题目中target=2,每个篮子只能装一种类型的水果,每种类型的水果用不同的数字表示。你在装水果的时候必须连续装,直到装不下为止,因为每棵树上对应的是一种类型的水果,其实返回的值就是连续经过的树的数量,连续经过的树中的不同的值的统计和不能大于2,求连续经过的树的数量。
代码
class Solution {
public int totalFruit(int[] fruits) {
int n=fruits.length;
int[] a=new int[n];
int i=0;
int num=0;
int ans=0;
for(int j=0;j<n;j++)
{
a[fruits[j]]++;
if(a[fruits[j]]==1)
num++;
while(num>2)
{
a[fruits[i]]--;
if(a[fruits[i]]==0)
num--;
i++;
}
ans=Math.max(ans,j-i+1);
}
return ans;
}
}
滑动窗口的具体思路在例题中已经讲过了,所以我在这个练习题中结合着代码来讲解。首先,这个题目的第一个点在于我们如何判断是否装过这个类型的水果,所以我是用一个数组来标识是否装过这个水果,数组为a[i]=x,i代表的是水果的类型,x代表装过几次。然后我们设置两个变量i、j,i代表连续经过的树的起始点,j代表终止点。然后我们使用j遍历fruits数组,每当a[fruits[j]]的值为1时,代表我们新装了一种水果类型,所以num需要加1,num代表的是水果类型。当num>2时,也到了滑动窗口算法需要判断的临界值,所以我们开始选择移动i,因为i一开始的值也是有效的,所以我们需要先判断i所代表的情况,然后在进行i加1,i代表我们需要去除fruits[i]这种类型的水果,所以a[fruits[i]]–。当a[fruits[i]]==0时,代表篮子中已经没有了这种水果类型,所以num-1。
因例题中是满足子数组的总和大于等于target,所以我们在判断一开始就要比较子数组的大小,但是这道题的要求是水果的种类需要小于等于2,而我们的判断逻辑是水果的种类大于2,所以在判断完while逻辑后,再比较大小。