questions M-R
This commit is contained in:
215
backend/data/questions/minimum-size-subarray-sum.yaml
Normal file
215
backend/data/questions/minimum-size-subarray-sum.yaml
Normal file
@@ -0,0 +1,215 @@
|
||||
title: Minimum Size Subarray Sum
|
||||
slug: minimum-size-subarray-sum
|
||||
difficulty: medium
|
||||
leetcode_id: 209
|
||||
leetcode_url: https://leetcode.com/problems/minimum-size-subarray-sum/
|
||||
categories:
|
||||
- arrays
|
||||
- binary-search
|
||||
patterns:
|
||||
- sliding-window
|
||||
- binary-search
|
||||
- prefix-sum
|
||||
|
||||
description: |
|
||||
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.
|
||||
|
||||
A **subarray** is a contiguous non-empty sequence of elements within an array.
|
||||
|
||||
constraints: |
|
||||
- `1 <= target <= 10^9`
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `1 <= nums[i] <= 10^4`
|
||||
|
||||
examples:
|
||||
- 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."
|
||||
- input: "target = 4, nums = [1,4,4]"
|
||||
output: "1"
|
||||
explanation: "The subarray [4] satisfies the condition with length 1."
|
||||
- input: "target = 11, nums = [1,1,1,1,1,1,1,1]"
|
||||
output: "0"
|
||||
explanation: "No subarray sums to 11 or greater (total sum is only 8)."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
Imagine you have a **stretchy window** that you slide across the array. The window has two ends: a left boundary and a right boundary. Your goal is to find the smallest window whose elements sum to at least `target`.
|
||||
|
||||
Here's the key insight: since all numbers are **positive**, adding more elements to the window can only **increase** the sum, and removing elements can only **decrease** it. This monotonic property is what makes the sliding window technique work.
|
||||
|
||||
Think of it like this: you start by expanding the right end of the window, adding elements until the sum reaches the target. Once it does, you try to **shrink** the window from the left to see if you can achieve the same sum with fewer elements. You keep track of the smallest valid window you find.
|
||||
|
||||
This "expand then contract" pattern is the essence of the **sliding window** technique for finding minimum-length subarrays.
|
||||
|
||||
approach: |
|
||||
We solve this using the **Sliding Window** technique:
|
||||
|
||||
**Step 1: Initialise variables**
|
||||
|
||||
- `left = 0`: Left boundary of our window
|
||||
- `current_sum = 0`: Running sum of elements in the window
|
||||
- `min_length = infinity`: Track the smallest valid window (use infinity so any valid window becomes the minimum)
|
||||
|
||||
|
||||
|
||||
**Step 2: Expand the window by moving the right pointer**
|
||||
|
||||
- Iterate `right` from `0` to `n - 1`
|
||||
- Add `nums[right]` to `current_sum` — this expands the window
|
||||
- After each expansion, check if the sum meets or exceeds `target`
|
||||
|
||||
|
||||
|
||||
**Step 3: Contract the window while the sum is valid**
|
||||
|
||||
- While `current_sum >= target`:
|
||||
- Update `min_length` with the current window size: `right - left + 1`
|
||||
- Remove `nums[left]` from `current_sum` — this shrinks the window
|
||||
- Move `left` forward: `left += 1`
|
||||
- This inner loop finds the minimum window ending at the current `right` position
|
||||
|
||||
|
||||
|
||||
**Step 4: Return the result**
|
||||
|
||||
- If `min_length` is still infinity, no valid subarray exists — return `0`
|
||||
- Otherwise, return `min_length`
|
||||
|
||||
|
||||
|
||||
The sliding window works because all elements are positive: once the sum drops below `target`, we need to expand right again (no point shrinking further).
|
||||
|
||||
common_pitfalls:
|
||||
- title: Using a Brute Force Nested Loop
|
||||
description: |
|
||||
A naive approach tries every possible subarray:
|
||||
- Outer loop for start index
|
||||
- Inner loop for end index
|
||||
- Sum calculation for each subarray
|
||||
|
||||
This results in **O(n^2)** or **O(n^3)** time complexity. With `nums.length <= 10^5`, this will cause **Time Limit Exceeded (TLE)**.
|
||||
|
||||
The sliding window achieves O(n) because each element is added and removed at most once.
|
||||
wrong_approach: "Nested loops for all subarrays"
|
||||
correct_approach: "Sliding window with two pointers"
|
||||
|
||||
- title: Forgetting Elements Are Positive
|
||||
description: |
|
||||
The sliding window technique **only works here because all elements are positive**. This guarantees:
|
||||
- Expanding the window always increases the sum
|
||||
- Shrinking the window always decreases the sum
|
||||
|
||||
If negative numbers were allowed, you couldn't shrink the window confidently — removing a negative number would increase the sum!
|
||||
|
||||
For arrays with negatives, you'd need a different approach (like prefix sums with binary search or monotonic deque).
|
||||
wrong_approach: "Applying sliding window blindly to any subarray sum problem"
|
||||
correct_approach: "Verify elements are positive before using sliding window"
|
||||
|
||||
- title: Returning min_length Without Checking Validity
|
||||
description: |
|
||||
If no subarray sums to `target` or more, `min_length` remains infinity. Returning this would be incorrect.
|
||||
|
||||
Always check: `return 0 if min_length == infinity else min_length`
|
||||
|
||||
Example: `target = 11, nums = [1,1,1,1,1,1,1,1]` — total sum is 8, so no valid subarray exists.
|
||||
wrong_approach: "return min_length"
|
||||
correct_approach: "return 0 if min_length == float('inf') else min_length"
|
||||
|
||||
key_takeaways:
|
||||
- "**Sliding Window** is the go-to technique for contiguous subarray problems with monotonic conditions"
|
||||
- "**Positive elements enable shrinking**: You can confidently shrink the window because removing elements always decreases the sum"
|
||||
- "**Two-pointer pattern**: The left pointer never moves backward — each element is processed at most twice (once added, once removed)"
|
||||
- "**O(n) despite nested loops**: The inner while loop is amortized O(1) because `left` moves at most `n` times total"
|
||||
|
||||
time_complexity: "O(n). Each element is added to the window once (when `right` passes it) and removed at most once (when `left` passes it). The inner while loop executes at most `n` times total across all iterations."
|
||||
space_complexity: "O(1). We only use a fixed number of variables (`left`, `current_sum`, `min_length`) regardless of input size."
|
||||
|
||||
solutions:
|
||||
- approach_name: Sliding Window
|
||||
is_optimal: true
|
||||
code: |
|
||||
def min_subarray_len(target: int, nums: list[int]) -> int:
|
||||
n = len(nums)
|
||||
left = 0
|
||||
current_sum = 0
|
||||
min_length = float('inf') # Use infinity so any valid window becomes min
|
||||
|
||||
# Expand window by moving right pointer
|
||||
for right in range(n):
|
||||
current_sum += nums[right] # Add element to window
|
||||
|
||||
# Contract window while sum is valid
|
||||
while current_sum >= target:
|
||||
# Update minimum length
|
||||
min_length = min(min_length, right - left + 1)
|
||||
# Remove leftmost element and shrink window
|
||||
current_sum -= nums[left]
|
||||
left += 1
|
||||
|
||||
# Return 0 if no valid subarray found
|
||||
return 0 if min_length == float('inf') else min_length
|
||||
explanation: |
|
||||
**Time Complexity:** O(n) — Each element is added and removed from the window at most once.
|
||||
|
||||
**Space Complexity:** O(1) — Only a few variables needed.
|
||||
|
||||
The sliding window expands by moving `right` and contracts by moving `left`. Because all elements are positive, once the sum drops below `target`, we must expand again. This guarantees optimal efficiency.
|
||||
|
||||
- approach_name: Binary Search with Prefix Sums
|
||||
is_optimal: false
|
||||
code: |
|
||||
import bisect
|
||||
|
||||
def min_subarray_len(target: int, nums: list[int]) -> int:
|
||||
n = len(nums)
|
||||
min_length = float('inf')
|
||||
|
||||
# Build prefix sum array: prefix[i] = sum of nums[0..i-1]
|
||||
prefix = [0] * (n + 1)
|
||||
for i in range(n):
|
||||
prefix[i + 1] = prefix[i] + nums[i]
|
||||
|
||||
# For each starting position, binary search for the ending position
|
||||
for i in range(n):
|
||||
# We need prefix[j] - prefix[i] >= target
|
||||
# So prefix[j] >= prefix[i] + target
|
||||
needed = prefix[i] + target
|
||||
# Find smallest j where prefix[j] >= needed
|
||||
j = bisect.bisect_left(prefix, needed)
|
||||
|
||||
if j <= n: # Valid ending position found
|
||||
min_length = min(min_length, j - i)
|
||||
|
||||
return 0 if min_length == float('inf') else min_length
|
||||
explanation: |
|
||||
**Time Complexity:** O(n log n) — Building prefix sums is O(n), and we do n binary searches of O(log n) each.
|
||||
|
||||
**Space Complexity:** O(n) — For the prefix sum array.
|
||||
|
||||
This approach builds a prefix sum array, then for each starting index, binary searches for the smallest ending index that achieves the target sum. Works because prefix sums are monotonically increasing (all elements positive).
|
||||
|
||||
- approach_name: Brute Force
|
||||
is_optimal: false
|
||||
code: |
|
||||
def min_subarray_len(target: int, nums: list[int]) -> int:
|
||||
n = len(nums)
|
||||
min_length = float('inf')
|
||||
|
||||
# Try every starting position
|
||||
for i in range(n):
|
||||
current_sum = 0
|
||||
# Extend to every ending position
|
||||
for j in range(i, n):
|
||||
current_sum += nums[j]
|
||||
if current_sum >= target:
|
||||
min_length = min(min_length, j - i + 1)
|
||||
break # Found minimum for this start, move on
|
||||
|
||||
return 0 if min_length == float('inf') else min_length
|
||||
explanation: |
|
||||
**Time Complexity:** O(n^2) — Nested loops over all starting and ending positions.
|
||||
|
||||
**Space Complexity:** O(1) — Only tracking current sum and minimum.
|
||||
|
||||
This checks every possible subarray starting position, extending until the sum meets the target. The `break` optimises slightly (we stop once we hit target), but worst case is still O(n^2). Too slow for large inputs.
|
||||
Reference in New Issue
Block a user