力扣题解 209. 长度最小的子数组

96 阅读2分钟

image.png

image.png

一、这是一个时间复杂度O(n)的算法。

利用每次遍历一个元素时,如果当前的sum>=target,就会从i到j的范围内找一个更小的子序列,这个子序列的末尾是nums[j],判断这个子序列的和是否满足sum>=target,因为i始终不会超过当前的j,所以降低了时间复杂度,最差情况下,i和j都遍历n次,即2*n,所以是O(n)时间复杂度。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int result=100001, subLen=0;
        int i=0, j=0, sum=0;
        for(; j<nums.length; j++){
            sum+=nums[j];
            while(sum>=target){
                subLen=j-i+1;
                result=Math.min(result,subLen);
                sum-=nums[i++];
            }
        }
        return result==100001 ? 0:result;
    }
}
题目还要求实现O(nlogn)时间复杂度的算法

先说一下暴力搜索的算法,分别遍历每个元素并且以当前遍历的元素开始逐个遍历剩下的元素,所以时间复杂度是O(n^2)。所以改进的搜索方法是使用二分搜索。

说一下前缀和:其中 sums[i]表示从 nums[0] 到 nums[i−1] 的元素和。

题目要求的是最小长度子数组之和大于等于target,所以利用target<=sums[j]-sums[i],得到最小的数组长度是j-i。

这里有个地方得讲明白:如果index<0,说明前缀和数组中没有这个查找的值,所以二分查找返回的是负数,需要转成整数然后加1,得到的结果是前缀和数组中比s大的值所在的下标。所以在之后要判断index<=len是否成立。防止index越界。

class Solution {
        public int minSubArrayLen(int target, int[] nums) {
        int len=nums.length;
        int result=100001;
        int[] sums=new int[len+1];
        for(int i=1; i<=len; i++){
            sums[i]=sums[i-1]+nums[i-1];
        }

        for(int i=0; i<=len; i++){
            int s=target+sums[i];
            int index=Arrays.binarySearch(sums,s);
            if(index<0){
                index=-index-1;
            }
            if(index<=len){
                result=Math.min(result, index-i);
            }
        }
        return result==100001? 0: result;
    }
}