leetcode-长度最小的子数组

122 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

题目描述

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

 

示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:
输入:target = 4, nums = [1,4,4]
输出:1

示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

思路

我们先想直接的暴力方法,遍历数组,把当前元素作为字数组的开始元素i,然后向后找,直到找到结束元素j,使得 nums[i]nums[j]\sum_{nums[i]}^{nums[j]} >= target。那么,nums[i]~nums[j]就是一个符合题意的子数组,我们的目标就是求解这样所有符合条件的字数组的最小长度,如果不存在,就返回0。这样的暴力方法,时间复杂度是O(n^2),可能是超时。
仔细想一下上面的过程,我们发现了一些重复的计算,比如数据中的示例1,我们以第0个元素开头,第3个元素结束的子数组2,3,1,2和为8,是符合要求的;然后我们以1个元素开头,第4个元素结束的子数组3,1,2,4和为10,也是符合要求的,但是我们发现,相同的是3,1,2这部分,我们求和了2遍。
求解符合条件的子数组的过程,我们可以用图表示出来:
滑动窗口过程.png
看了这个图,应该比较容易发现,这题可以使用滑动窗口:定义2个指针start和end,初始都指向nums[0],然后如果当前窗口和sum < target,那么就让end往右滑;如果sum >= target,下一步就让start往右滑,这样我们可以在O(n)的时间复杂度下,求的所有的符合条件的子数组。 注意,初始化的时候,我们定义符合条件的子数组为Integer的最大值,然后不断去减小,但是要注意不存在符合条件子数组的情况,所以最后返回前,还要打一个补丁,如果还是Integer的最大值,要将ans改为0。

Java版本代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int ans = Integer.MAX_VALUE;
        int start = 0, end = 0;
        int sum = 0;
        int len = nums.length;
        while (end < len) {
            sum += nums[end];
            while (sum >= target && start <= end) {
                ans = Integer.min(ans, end-start+1);
                sum -= nums[start];
                start++;
            }
            end++;
        }
        // ans还是初始值,证明没有符合条件的子数组
        if (ans == Integer.MAX_VALUE) {
            ans = 0;
        }
        return ans;
    }   
}