Java描述 LeetCode,209. Minimum Size Subarray Sum 长度最小的子数组之和,滑动窗口;前缀和+二分查找;

本文介绍了如何使用滑动窗口和前缀和两种方法解决寻找数组中和大于等于目标值的最小子数组长度的问题。分别展示了两种不同的滑动窗口实现,并探讨了前缀和结合二分查找的解决方案,详细解释了每种方法的思路和代码实现,同时分析了它们的时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家好,我是河海哥,专注于后端,如果可以的话,想做一名code designer而不是普通的coder,一起见证河海哥的成长,您的评论与赞与关注是我的最大动力,如有错误还请不吝赐教,万分感谢。一起支持原创吧!纯手打有笔误还望谅解。

1-1:题目描述

Given an array of positive integers nums and a positive integer target, return the minimal length of a contiguous subarray [numsl, numsl+1, …, numsr-1, numsr] of which the sum is greater than or equal to target. If there is no such subarray, return 0 instead.

Example 1:

Input: target = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: The subarray [4,3] has the minimal length under the problem constraint.

Example 2:

Input: target = 4, nums = [1,4,4]
Output: 1

Example 3:

Input: target = 11, nums = [1,1,1,1,1,1,1,1]
Output: 0

Constraints:

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

Follow up: If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log(n)).

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目理解:
在一个数组中间找到最小的数组,它的和=target就行。

1-2:滑动窗口解法

在这里插入图片描述
☘️算法流程:
核心思想是用一个可伸缩的滑动窗口去控制子数组的大小。整个窗口是左闭右闭型,[start, end],每次这种范围型的题目,一定要先自己想好自己的范围在哪?区间开闭? start控制窗口的头,end控制窗口的尾,每次start都往前面推一个,代表窗口在移动,end控制窗口的值的总和大小,一旦窗口内的和超过了target,end就结束,数组的长度end - start + 1。

☘️整体的思路并不难,难就难在对边界的掌握。我是没有能力一步就写到位的,只能通过debug去多次尝试。

public static int minSubArrayLen(int target, int[] nums) {
    int n = nums.length;
    int minLength = Integer.MAX_VALUE;
    int start = 0;
    int end = -1;
    int sum = 0;
    while (start < n) {
        while (sum < target && end != n - 1) {
            end++;
            sum += nums[end];
        }
        if (sum >= target) {
            minLength = Math.min(minLength, end - start + 1);
        }
        sum -= nums[start];
        start++;
    }
    return minLength == Integer.MAX_VALUE ? 0 : minLength;
}

时间复杂度O(N)
空间复杂度O(1)

☘️答案的思路和我的似乎不太一样,它是维持一个sum和,每次都通过end往sum里添加数,当达到target的时候,就通过start对这个窗口进行瘦身,把它变成和刚好<target,只要再来几个数就能满足和大于target。不明白可以看一下官方解答的图,其实和上面的大同小异。代码如下:

public static int minSubArrayLen2(int target, int[] nums) {
    int n = nums.length;
    if (n == 0) {
        return 0;
    }
    int minLength = Integer.MAX_VALUE;
    int start = 0;
    int end = 0;
    int sum = 0;
    while (end < n) {
        sum += nums[end];
        while (sum >= target) {
            minLength = Math.min(minLength, end - start + 1);
            sum -= nums[start];
            start++;
        }
        end++;
    }
    return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
时间复杂度O(N)
空间复杂度O(1)

1-3:前缀和+二分查找

☘️我这里的和答案有一点区别,我这里前缀和是啥呢?prefix[i] = nums[0:i]之和,左闭区间,右闭区间。比如prefix[1] = nums[0] + nums[1];

☘️我们先给出一般情况下的示意图:
在这里插入图片描述
☘️由于nums数组中的值都是整数,所以前缀数组必然是递增的,这样就可以找到符合要求的bound,什么样的bound符合要求,在上面写了。所以整个算法有这样几个步骤:

  • 填充prefix前缀和数组
  • 从前缀和第一个位置0开始,二分查找符合要求的bound,并且计算数组区间长度加以更新。
public static int minSubArrayLen3(int target, int[] nums) {
    int n = nums.length;
    if (n == 0) {
        return 0;
    }
    int minLength = Integer.MAX_VALUE;
    int[] prefix = new int[n]; // prefix[i] : nums[0:i] 之和
    int sum = 0;
    // fill prefix[]
    for (int i = 0; i < n; i++) {
        sum += nums[i];
        prefix[i] = sum;
    }
    for (int i = 0; i < n; i++) {
        int val = (i == 0 ? target : target + prefix[i - 1]);
        int bound = Arrays.binarySearch(prefix, val); // bound = -insertion point - 1
        // can't find the val
        if (bound < 0) {
            bound = -bound - 1;
        }
        // if find the val, the scope of bound is from 0 to n-1. [0:n-1]
        if (bound <= n - 1) {
            minLength = Math.min(minLength, bound - i + 1);
        }
    }
    return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
时间复杂度O(nlogn)
空间复杂度O(n)

☘️这里要特别注意java的二分查找,如果找到返回目标的index,如果找不到,就返回-该插入的位置的索引-1。为啥这样呢?比如0的时候,不-1就不是负号了,秒啊!所以<0就是没找到。然后再计算出符合要求的bound,这里的“插入的位置的索引”就是刚好前缀和>target的值。可以debug试试,你就知道我在说啥了。比如:

int[] nums = new int[]{2, 3, 1, 2, 4, 3};
int target = 7;

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

河海哥yyds

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值