leetcode
leetcode 151 ~ 200
长度最小的子数组

长度最小的子数组

难度:

标签:

题目描述

给定一个含有 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

 

进阶:

  • 如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

代码结果

运行时间: 29 ms, 内存: 26.2 MB


/*
 * 思路:
 * 使用Java Stream API来处理数组。首先将数组转换为Stream,然后使用Stream的特性来找到最短的子数组。
 * 我们不能直接使用Stream来实现滑动窗口,所以这里先计算出每个前缀和,然后再寻找符合条件的子数组长度。
 */
import java.util.Arrays;
import java.util.OptionalInt;
 
public class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        int[] prefixSums = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefixSums[i + 1] = prefixSums[i] + nums[i];
        }
        OptionalInt minLength = Arrays.stream(prefixSums).boxed()
            .flatMap(i -> Arrays.stream(prefixSums)
            .skip(i + 1)
            .filter(j -> j - i >= target)
            .map(j -> j - i))
            .min(Integer::compareTo);
        return minLength.isPresent() ? minLength.getAsInt() : 0;
    }
}

解释

方法:

该题解使用双指针滑动窗口的思路来解决问题。通过左右两个指针构成一个窗口,右指针不断向右移动扩大窗口,累加窗口内的元素总和,当总和大于等于目标值时,开始收缩窗口,移动左指针缩小窗口,并更新最小子数组长度,直到窗口内的总和小于目标值,然后继续扩大窗口,重复上述过程,直到右指针到达数组末尾。最终返回找到的最小子数组长度。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在累积总和大于等于目标值后,需要通过移动左指针来缩小窗口?这样做的目的是什么?
当累积总和大于等于目标值时,移动左指针来缩小窗口的目的是为了寻找可能存在的更小长度的符合条件的子数组。这是因为当前的窗口已经满足了条件,进一步缩小这个窗口可能会找到一个更短的满足条件的子数组,从而更新最小子数组的长度。此外,这也有助于在总和超过目标值很多的情况下,去除不必要的部分,使窗口的长度尽可能地小。
🦆
在题解中提到,当总和大于等于目标值时,会尝试更新最小子数组长度。如果存在多个长度相同的子数组满足条件,这种方法是否能保证找到所有这样的子数组?
此算法的主要目的是找到满足条件的最小长度的子数组,而不是找出所有可能的满足条件的子数组。虽然算法会遍历并检查每个可能的窗口,但它只记录最小长度。因此,如果存在多个长度相同的符合条件的子数组,该算法不保证会记录所有这些子数组,而只保证会找到至少一个这样的子数组。
🦆
题解中提到,如果最小子数组长度为无穷大,则返回0。这种情况是如何判断的?在什么情况下,最小子数组长度会保持为无穷大?
在算法开始时,最小子数组长度被初始化为无穷大(使用float('inf'))。这是一个标记值,用来表示尚未找到任何满足条件的子数组。如果在遍历整个数组后,这个值仍然是无穷大,意味着没有任何子数组的总和达到或超过目标值。在这种情况下,返回0表示不存在符合条件的子数组。
🦆
在初始化最小子数组长度为无穷大时,使用了`float('inf')`。使用这种方法有什么特别的考虑吗,比如在某些编程环境下有可能不支持这种表示吗?
使用`float('inf')`在大多数现代编程环境中是支持的,它提供了一种便捷的方式来表示无穷大,使得任何实际长度的子数组都会小于这个值,从而可以在找到第一个满足条件的子数组时立即更新这个最小长度。虽然在极少数环境中可能不支持这种表示,但这种情况非常罕见。在这些环境中,可以考虑使用一个足够大的数(如数组长度加一)来代替无穷大。

相关问题

最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

 

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

 

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

 

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 105
  • st 由英文字母组成

 

进阶:你能设计一个在 o(m+n) 时间内解决此问题的算法吗?

和等于 k 的最长子数组长度

和等于 k 的最长子数组长度

最长重复子数组

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 

 

示例 1:

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。

示例 2:

输入:nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0]
输出:5

 

提示:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 100