questions M-R

This commit is contained in:
2025-05-25 12:43:25 +01:00
parent 5dbe52df0d
commit ddceeec07e
62 changed files with 12841 additions and 0 deletions

View 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)
&nbsp;
**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`
&nbsp;
**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
&nbsp;
**Step 4: Return the result**
- If `min_length` is still infinity, no valid subarray exists — return `0`
- Otherwise, return `min_length`
&nbsp;
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.