給定正整數數組 nums 和目標值 s,找出總和 ≥ s 的連續子數組的最小長度。不存在返回 0。
示例:nums = [2,3,1,2,4,3], s = 7 → 子數組 [4,3] 長度 2 為最小。
因所有數為正數,窗口擴大時和單調遞增,縮小時單調遞減。維護窗口 [left, right] 使其和 ≥ s,不斷嘗試縮小左邊界更新答案。
left = 0, sum = 0, minLen = INFright = 0 .. n-1:
sum += nums[right]sum >= s 時:
minLen = min(minLen, right - left + 1)sum -= nums[left++] (縮小左邊界)minLen == INF ? 0 : minLenpublic int minSubArrayLen(int s, int[] nums) {
int n = nums.length;
int left = 0, sum = 0;
int minLen = n + 1; // 不可能超過 n
for (int right = 0; right < n; ++right) {
sum += nums[right];
while (sum >= s) {
minLen = Math.min(minLen, right - left + 1);
sum -= nums[left++];
}
}
return minLen == n + 1 ? 0 : minLen;
}
int minSubArrayLen(int s, vector& nums) { int n = nums.size(); int left = 0, sum = 0; int minLen = n + 1; for (int right = 0; right < n; ++right) { sum += nums[right]; while (sum >= s) { minLen = min(minLen, right - left + 1); sum -= nums[left++]; } } return minLen == n + 1 ? 0 : minLen; }
left 和 right 各自單調遞增,最多各走 n 步若數組含負數,滑動窗口失效。但本題全正數,仍可展示另一思路:
pre[i] = sum(nums[0..i-1]),pre[0]=0,遞增i,二分查找最大 j ≤ i 使 pre[i] - pre[j] ≥ s,即 pre[j] ≤ pre[i] - si - j,取最小int minSubArrayLenBinary(int s, vector& nums) { int n = nums.size(); vector pre(n + 1, 0); for (int i = 1; i <= n; ++i) pre[i] = pre[i-1] + nums[i-1]; int minLen = n + 1; for (int i = 1; i <= n; ++i) { int target = pre[i] - s; // 找最後一個 ≤ target 的位置 int j = upper_bound(pre.begin(), pre.begin() + i, target) - pre.begin() - 1; if (j >= 0) minLen = min(minLen, i - j); } return minLen == n + 1 ? 0 : minLen; }
| 指標 | 滑動窗口 O(n) | 前綴和+二分 O(n log n) |
|---|---|---|
| 適用條件 | 全正數 | 任意整數 (含負數) |
| 實現難度 | 簡單 | 中等 |
| 實際速度 | 極快 | 較快 |
| 空間 | O(1) | O(n) |
滑動窗口是處理「正數數組子區間和/積約束」的利器,掌握其單調性依賴與雙指針協同移動模式,可秒殺大批同類題目。