【57.长度最小的子数组】

82 阅读1分钟

题目

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

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

示例 1:

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

题解

方式一:滑动窗口

public int minSubArrayLen(int target, int[] nums) {
    int l = 0; // 左边界
    int sum = 0;
    int result = Integer.MAX_VALUE;
    for (int r = 0; r < nums.length; r++) { // 右边界
        sum += nums[r];
        while (sum >= target && l <= r) {
            // 满足条件时,不断缩小窗口,知道不满足条件
            result = Math.min(result, r - l + 1);
            sum -= nums[l++];
        }
    }
    return result == Integer.MAX_VALUE ? 0 : result;
}

方式二:前缀和 + 二分

public int minSubArrayLen(int target, int[] nums) {
    int n = nums.length;
    int[] preSum = new int[n + 1];
    for (int i = 0; i < n; i++) {
        // 前缀和数组一定有序,所以能使用二分
        preSum[i + 1] = preSum[i] + nums[i];
    }
    int result = Integer.MAX_VALUE;
    for (int i = 0; i < n; i++) {
        // 找到差值大于等于target的下标
        int j = binary(preSum, preSum[i] + target);
        if (j != -1) {
            // 如果存在,更新结果
            result = Math.min(result, j - i);
        }
    }
    return result == Integer.MAX_VALUE ? 0 : result;
}

public int binary(int[] preSum, int target) {
    int l = 0;
    int r = preSum.length - 1;
    int result = -1;
    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (preSum[mid] < target) {
            l = mid + 1;
        } else {
            result = mid;
            r = mid - 1;
        }
    }
    return result;
}

总结

算法:双指针前缀和二分