209. 长度最小的子数组
问题描述
给定一个含有 n 个正整数的数组和一个正整数 target,找出数组中满足其和 ≥ target 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 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
算法思路
滑动窗口(双指针):
- 初始化:
- 左指针
left = 0 - 当前窗口和
windowSum = 0 - 最小长度
minLen = Integer.MAX_VALUE
- 左指针
- 移动右指针:
- 遍历数组(右指针
right从 0 开始) - 将
nums[right]加入窗口和windowSum
- 遍历数组(右指针
- 收缩左边界:
- 当
windowSum ≥ target时:- 更新最小长度
minLen = min(minLen, right-left+1) - 从窗口和中减去
nums[left] - 左指针右移
left++
- 更新最小长度
- 当
- 返回结果:
- 若
minLen仍是初始值,返回 0 - 否则返回
minLen
- 若
代码实现
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0; // 窗口左指针
int windowSum = 0; // 当前窗口和
int minLen = Integer.MAX_VALUE; // 最小长度(初始设为极大值)
for (int right = 0; right < nums.length; right++) {
// 将右指针元素加入窗口
windowSum += nums[right];
// 当窗口和满足条件时收缩左边界
while (windowSum >= target) {
// 更新最小长度
minLen = Math.min(minLen, right - left + 1);
// 移除左边界元素并右移左指针
windowSum -= nums[left];
left++;
}
}
// 若未找到有效子数组返回0,否则返回最小长度
return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
}
算法分析
- 时间复杂度:O(n)
每个元素最多被访问两次(右指针加入窗口一次,左指针移除一次) - 空间复杂度:O(1)
仅使用常数空间
算法过程
target=7, nums=[2,3,1,2,4,3] :
步骤 窗口 窗口和 操作
0 [2] 2 不满足
1 [2,3] 5 不满足
2 [2,3,1] 6 不满足
3 [2,3,1,2] 8 满足 → 记录长度4,左指针右移
[3,1,2] 6 不满足
4 [3,1,2,4] 10 满足 → 记录长度4,左指针右移
[1,2,4] 7 满足 → 记录长度3,左指针右移
[2,4] 6 不满足
5 [2,4,3] 9 满足 → 记录长度3,左指针右移
[4,3] 7 满足 → 记录长度2(最小),左指针右移
[3] 3 不满足
结果:minLen=2
测试用例
public static void main(String[] args) {
Solution solution = new Solution();
// 测试用例1:标准示例
int[] nums1 = {2,3,1,2,4,3};
System.out.println("Test 1: " + solution.minSubArrayLen(7, nums1)); // 2
// 测试用例2:单个元素满足
int[] nums2 = {1,4,4};
System.out.println("Test 2: " + solution.minSubArrayLen(4, nums2)); // 1
// 测试用例3:需整个数组
int[] nums3 = {1,2,3,4,5};
System.out.println("Test 3: " + solution.minSubArrayLen(15, nums3)); // 5
// 测试用例4:无解
int[] nums4 = {1,1,1,1};
System.out.println("Test 4: " + solution.minSubArrayLen(10, nums4)); // 0
// 测试用例5:多个解取最小
int[] nums5 = {1,2,3,4,1,2,3};
System.out.println("Test 5: " + solution.minSubArrayLen(7, nums5)); // 2([3,4]或[4,1,2]取最小)
// 测试用例6:边界值
int[] nums6 = {};
System.out.println("Test 6: " + solution.minSubArrayLen(1, nums6)); // 0
}
关键点
-
滑动窗口条件:
- 右指针移动:扩大窗口直至满足条件
- 左指针移动:在满足条件时收缩窗口寻找最小长度
-
正整数的关键作用:- 保证窗口扩展时和
单调递增 - 收缩窗口时和
单调递减
- 保证窗口扩展时和
-
边界处理:
- 空数组直接返回 0
- 未找到解时返回 0(通过
minLen初始值判断)
常见问题
-
为什么使用滑动窗口?
数组元素均为正整数,具有单调性,适合用滑动窗口在 O(n) 时间复杂度内解决。 -
如何处理多个解的情况?
在满足条件时持续收缩左边界,记录所有可能的最小长度。 -
如果数组包含负数/零?
滑动窗口失效,需使用前缀和+二分查找(时间复杂度 O(n log n))。
399

被折叠的 条评论
为什么被折叠?



