长度最小的子数组

67 阅读2分钟

数组

长度最小的子数组

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

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

  • 暴力解法思路
    遍历数组,记录以nums[i]开头的满足条件的连续子数组的长度,不断更新该长度,直到找到最小的满足条件的连续子数组的长度。

  • 代码如下:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int res = nums.length + 1;
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i; j < nums.length; j++) {
                sum += nums[j];
                if (sum >= target){
                    res = Math.min(j - i + 1,res);
                    break;
                }
            }
            sum = 0;
        }
        if (res == nums.length + 1) return 0;
        return res;
    }
}

暴力解法的时间复杂度为O(n^2),空间复杂度为O(1)。

  • 滑动窗口解法思路
    滑动窗口:不断调节子序列的起始位置和终止位置,得到想要的结果。
    在暴力解法中,是一个for循环为滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环完成了一个不断搜索区间的过程。
    那么怎样仅仅使用一个for循环来完成这个操作?
    首先思考:
    如果使用一个for循环,那么应该表示滑动窗口的起始位置还是终止位置呢?
    如果表示起始位置,那终止位置难以表示,因此应该表示终止位置。
    那么起始位置应该如何表示呢?
    在遍历终止位置时,通过变量将遍历过的元素和记录下来,只要区间满足条件,就将初始位置指针右移,这样就实现了窗口的滑动。
  • 代码如下:
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int res = nums.length + 1;
        int sum = 0; // 滑动窗口数值之和
        int firstIndex = 0; // 滑动窗口起始位置
        for (int i = 0; i < nums.length; i++) {
                sum += nums[i];
            // 这里使用while,每次更新起始位置firstIndex,并不断比较子序列是否满足条件
            while (sum >= target) {
                res = Math.min(res, i - firstIndex + 1);
                sum -= nums[firstIndex];
                firstIndex++; // 不断变更子序列的起始位置
            }
        }
        if (res == nums.length + 1) return 0;
        return res;
    }
    }

根据当前子序列和的大小,不断调节子序列的起止位置。
时间复杂度为O(n),空间复杂度为O(1)。