「前端刷题」209.长度最小的子数组(MEDIUM)

837 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

题目(Minimum Size Subarray Sum)

链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
解决数:2473
通过率:48.5%
标签:数组 二分查找 前缀和 滑动窗口 
相关公司:goldman-sachs facebook amazon 

给定一个含有 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)) 时间复杂度的解法。

思路

  • 连续子数组可以表示为 [i,j][i,j]:从第 i 项到第 j 项。

  • [i,j][i,j] 子数组和 >= s,如果此时扩张窗口,条件就依然满足,但背离“最小长度”的要求。

    • 所以选择收缩窗口:i 右移,直到条件不再满足(是一个循环),这是在优化可行解,并让窗口长度挑战最小纪录。
  • 当窗口的和 < s,此时应该扩张窗口,j 右移,直到条件重新满足。

  • 扩张窗口:为了找到一个可行解,找到了就不再扩张,因为扩张不再有意义。

  • 收缩窗口:在长度上优化该可行解,直到条件被破坏。

  • 继续寻找下一个可行解,然后再优化到不能优化…… 代码

const minSubArrayLen = (s, nums) => {
  let minLen = Infinity;
  let i = 0;
  let j = 0;
  let sum = 0;
  while (j < nums.length) {   // 主旋律是扩张,找可行解
    sum += nums[j];
    while (sum >= s) {        // 间歇性收缩,优化可行解
      minLen = Math.min(minLen, j - i + 1);
      sum -= nums[i];
      i++;
    }
    j++;
  }
  return minLen === Infinity ? 0 : minLen; // 从未找到可行解,返回0
};
func minSubArrayLen(s int, nums []int) int {
    minLen := math.MaxInt32
    i, j := 0, 0
    sum := 0
    
    for j < len(nums) {
        sum += nums[j]
        for sum >= s {
            if j-i+1 < minLen {
                minLen = j-i+1
            }
            sum -= nums[i]
            i++
        }
        j++
    }
    if minLen == math.MaxInt32 {
        return 0
    }
    return minLen
}