Medium

题目描述

给定一个含有 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 <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^4

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

解题思路

这道题要求找到和大于等于目标值的最短连续子数组,有两种主要解法:

方法一:滑动窗口(推荐)

滑动窗口是这类问题的最优解法。我们维护一个窗口 [left, right],不断扩大右边界来增加窗口和,当窗口和达到目标值时,尝试缩小左边界来寻找更短的子数组。

具体步骤:

  1. 初始化左指针 left = 0,窗口和 sum = 0,最小长度 minLen = ∞
  2. 遍历右指针 right,将 nums[right] 加入窗口和
  3. 当窗口和 ≥ target 时,更新最小长度,然后收缩左边界
  4. 重复步骤 2-3 直到遍历完成

方法二:前缀和 + 二分查找

由于数组元素都是正数,前缀和数组严格递增,可以用二分查找优化。对于每个位置,计算以该位置为起点的最短子数组长度。

时间复杂度 O(n log n),空间复杂度 O(n)。虽然理论上较慢,但在某些场景下可能更直观。

推荐使用滑动窗口解法,代码简洁且效率最高。

代码实现

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0, sum = 0, minLen = INT_MAX;
        
        for (int right = 0; right < nums.size(); right++) {
            sum += nums[right];
            
            while (sum >= target) {
                minLen = min(minLen, right - left + 1);
                sum -= nums[left++];
            }
        }
        
        return minLen == INT_MAX ? 0 : minLen;
    }
};
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        left = 0
        sum_val = 0
        min_len = float('inf')
        
        for right in range(len(nums)):
            sum_val += nums[right]
            
            while sum_val >= target:
                min_len = min(min_len, right - left + 1)
                sum_val -= nums[left]
                left += 1
        
        return 0 if min_len == float('inf') else min_len
public class Solution {
    public int MinSubArrayLen(int target, int[] nums) {
        int left = 0, sum = 0, minLen = int.MaxValue;
        
        for (int right = 0; right < nums.Length; right++) {
            sum += nums[right];
            
            while (sum >= target) {
                minLen = Math.Min(minLen, right - left + 1);
                sum -= nums[left++];
            }
        }
        
        return minLen == int.MaxValue ? 0 : minLen;
    }
}
var minSubArrayLen = function(target, nums) {
    let left = 0;
    let sum = 0;
    let minLen = Infinity;
    
    for (let right = 0; right < nums.length; right++) {
        sum += nums[right];
        
        while (sum >= target) {
            minLen = Math.min(minLen, right - left + 1);
            sum -= nums[left];
            left++;
        }
    }
    
    return minLen === Infinity ? 0 : minLen;
};

复杂度分析

复杂度类型滑动窗口前缀和+二分
时间复杂度O(n)O(n log n)
空间复杂度O(1)O(n)

说明:

  • 滑动窗口:每个元素最多被访问两次(一次加入窗口,一次移出窗口)
  • 前缀和+二分:需要 O(n) 空间存储前缀和,每个位置进行二分查找需要 O(log n) 时间

相关题目