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:
  - slug: sliding-window
    is_optimal: false
  - slug: binary-search
    is_optimal: true
  - slug: prefix-sum
    is_optimal: false

function_signature: "def min_subarray_len(target: int, nums: list[int]) -> int:"

test_cases:
  visible:
    - input: { target: 7, nums: [2, 3, 1, 2, 4, 3] }
      expected: 2
    - input: { target: 4, nums: [1, 4, 4] }
      expected: 1
    - input: { target: 11, nums: [1, 1, 1, 1, 1, 1, 1, 1] }
      expected: 0
  hidden:
    - input: { target: 5, nums: [5] }
      expected: 1
    - input: { target: 6, nums: [5] }
      expected: 0
    - input: { target: 15, nums: [1, 2, 3, 4, 5] }
      expected: 5
    - input: { target: 100, nums: [1, 1, 1, 1, 1, 1, 1] }
      expected: 0
    - input: { target: 3, nums: [1, 1, 1, 1, 1] }
      expected: 3

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.
