LeetCdoe209.长度最小的子数组(滑动窗口例题,后有练习题)

LeetCode209.长度最小的子数组(滑动窗口例题,后有练习题)

题目

LeetCode题目链接

在这里插入图片描述

思路

这道题的暴力做法应该是非常容易想到的,我们可以设置两重循环,一重循环作为子数组的起始位置,一重循环作为子数组的终止位置,然后计算子数组的大小是否大于等于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.水果成篮)

LeetCode题目链接

在这里插入图片描述

思路(代码部分有具体的思路)

我认为这道题目的难点在于读题,有两个篮子——这就等于例题中说的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逻辑后,再比较大小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值