跳至主要內容

209. Minimum Size Subarray Sum


209. Minimum Size Subarray Sumopen in new window

🟠   🔖  数组 二分查找 前缀和 滑动窗口  🔗 LeetCodeopen in new window

题目

Given an array of positive integers nums and a positive integer target, return the minimal length of a __subarray whose 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 <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^4

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)).

题目大意

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

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

子数组是数组中连续的非空元素序列。

解题思路

思路一:滑动窗口

  1. 初始化变量:使用两个指针(或滑动窗口)来遍历数组;
  2. 扩大窗口:移动右指针,向右扩大求和;
  3. 缩小窗口:一旦窗口总和大于等于 target,就移动左指针缩小窗口;
  4. 更新最小长度:过程中更新最小长度;
  • 时间复杂度:O(n),其中 n 是数组的长度;
  • 空间复杂度:O(1)

思路二:暴力查找

  1. 初始化子数组的最小长度为无穷大;
  2. 枚举数组 nums 中的每个下标作为子数组的开始下标;
  3. 对于每个开始下标 i,需要找到大于或等于 i 的最小下标 j,使得从 nums[i]nums[j] 的元素和大于或等于 s
  4. 更新子数组的最小长度(此时子数组的长度是 j−i+1);
  • 时间复杂度:O(n^2),需要遍历每个下标作为子数组的开始下标,对于每个开始下标,需要遍历其后面的下标;
  • 空间复杂度:O(1)

思路三:二分查找

  1. 暴力查找的时间复杂度是 O(n^2),因为在确定每个子数组的开始下标后,找到长度最小的子数组需要 O(n) 的时间。如果使用二分查找,则可以将时间优化到 O(logn)
  2. 为了使用二分查找,需要额外创建一个数组 sums 用于存储数组 nums 的前缀和,其中 sums[i] 表示从 nums[0]nums[i−1] 的元素和。
  3. 得到前缀和之后,对于每个开始下标 i,可通过二分查找得到大于或等于 i 的最小下标 bound,使得 sums[bound]−sums[i−1]≥s,并更新子数组的最小长度(此时子数组的长度是 bound−(i−1))。

因为这道题保证了数组中每个元素都为正,所以前缀和一定是递增的,这一点保证了二分的正确性。如果题目没有说明数组中每个元素都为正,这里就不能使用二分来查找这个位置了。

  • 时间复杂度:O(nlogn),遍历每个下标的时间复杂度是 O(n),二分查找的时间复杂度是 O(logn),因此总时间复杂度是 O(nlogn)

  • 空间复杂度:O(n),额外创建数组 sums 存储前缀和。

代码

滑动窗口
/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function (target, nums) {
	let left = 0,
		sum = 0,
		minLength = Infinity;
	for (let right = 0; right < nums.length; right++) {
		sum += nums[right];
		while (sum >= target) {
			minLength = Math.min(minLength, right - left + 1);
			sum -= nums[left];
			left++;
		}
	}
	return minLength == Infinity ? 0 : minLength;
};

相关题目

相关题目
- [76. 最小覆盖子串](./0076.md)
- [🔒 Maximum Size Subarray Sum Equals k](https://leetcode.com/problems/maximum-size-subarray-sum-equals-k)
- [718. 最长重复子数组](https://leetcode.com/problems/maximum-length-of-repeated-subarray)
- [1658. 将 x 减到 0 的最小操作数](https://leetcode.com/problems/minimum-operations-to-reduce-x-to-zero)
- [2090. 半径为 k 的子数组平均值](https://leetcode.com/problems/k-radius-subarray-averages)
- [2233. K 次增加后的最大乘积](https://leetcode.com/problems/maximum-product-after-k-increments)