title: Product of Array Except Self
slug: product-of-array-except-self
difficulty: medium
leetcode_id: 238
leetcode_url: https://leetcode.com/problems/product-of-array-except-self/
categories:
  - arrays
patterns:
  - prefix-sum

function_signature: "def product_except_self(nums: list[int]) -> list[int]:"

test_cases:
  visible:
    - input: { nums: [1, 2, 3, 4] }
      expected: [24, 12, 8, 6]
    - input: { nums: [-1, 1, 0, -3, 3] }
      expected: [0, 0, 9, 0, 0]
  hidden:
    - input: { nums: [2, 3] }
      expected: [3, 2]
    - input: { nums: [1, 1, 1, 1] }
      expected: [1, 1, 1, 1]
    - input: { nums: [0, 0] }
      expected: [0, 0]
    - input: { nums: [5, -2, 4] }
      expected: [-8, 20, -10]
    - input: { nums: [1, 2, 3, 4, 5] }
      expected: [120, 60, 40, 30, 24]
    - input: { nums: [0, 1, 2, 3] }
      expected: [6, 0, 0, 0]
    - input: { nums: [-1, -1, -1] }
      expected: [1, 1, 1]

description: |
  Given an integer array `nums`, return an array `answer` such that `answer[i]` is equal to the product of all the elements of `nums` except `nums[i]`.

  The product of any prefix or suffix of `nums` is **guaranteed** to fit in a **32-bit** integer.

  You must write an algorithm that runs in **O(n)** time and **without using the division operation**.

constraints: |
  - `2 <= nums.length <= 10^5`
  - `-30 <= nums[i] <= 30`
  - The product of any prefix or suffix of `nums` is guaranteed to fit in a 32-bit integer

examples:
  - input: "nums = [1,2,3,4]"
    output: "[24,12,8,6]"
    explanation: "answer[0] = 2×3×4 = 24, answer[1] = 1×3×4 = 12, answer[2] = 1×2×4 = 8, answer[3] = 1×2×3 = 6"
  - input: "nums = [-1,1,0,-3,3]"
    output: "[0,0,9,0,0]"
    explanation: "Any position except index 2 includes the zero, making the product 0. Position 2 excludes the zero: (-1)×1×(-3)×3 = 9"

explanation:
  intuition: |
    For each position `i`, we need the product of everything **except** `nums[i]`. If division were allowed, we could compute the total product and divide by `nums[i]`. But division is forbidden (and would fail with zeros anyway).

    Think of it differently: the product of "everything except position i" equals:
    - (product of everything **left** of i) × (product of everything **right** of i)

    This is the **prefix-suffix** insight! For each position:
    - **Prefix product**: multiply all elements from the start up to (but not including) position i
    - **Suffix product**: multiply all elements from position i+1 to the end

    If we can compute both efficiently, we just multiply them together.

    The clever optimisation: we can use the output array itself to store prefix products in a left-to-right pass, then multiply in suffix products with a right-to-left pass using a single running variable.

  approach: |
    We solve this using **Two-Pass Prefix/Suffix Products**:

    **Step 1: Initialise the answer array**

    - Create `answer` of length n, initialised to 1
    - This array will hold our final results

    &nbsp;

    **Step 2: Left pass — compute prefix products**

    - Track `left_product = 1` (product of elements to the left)
    - For each `i` from 0 to n-1:
      - Set `answer[i] = left_product` (product of all elements before i)
      - Update `left_product *= nums[i]` (include current element for next position)

    &nbsp;

    **Step 3: Right pass — multiply by suffix products**

    - Track `right_product = 1` (product of elements to the right)
    - For each `i` from n-1 down to 0:
      - Multiply `answer[i] *= right_product` (now contains prefix × suffix)
      - Update `right_product *= nums[i]` (include current element for next position)

    &nbsp;

    **Step 4: Return the answer**

    - Each `answer[i]` now contains the product of all elements except `nums[i]`

    &nbsp;

    Example walkthrough with `[1,2,3,4]`:
    - After left pass: `[1, 1, 2, 6]` (prefix products)
    - After right pass: `[24, 12, 8, 6]` (prefix × suffix)

  common_pitfalls:
    - title: Using Division
      description: |
        The problem explicitly forbids the division operator. Even if allowed, division fails when the array contains zeros (division by zero).

        The prefix-suffix approach works without division and handles zeros naturally.
      wrong_approach: "total_product // nums[i]"
      correct_approach: "Prefix product × suffix product"

    - title: Using Extra Space for Prefix and Suffix Arrays
      description: |
        A straightforward approach creates separate `prefix[]` and `suffix[]` arrays, using O(n) extra space.

        The optimal solution reuses the output array for prefix products and uses a single variable for the running suffix product — O(1) extra space.
      wrong_approach: "prefix = []; suffix = [] — O(n) extra space"
      correct_approach: "Use output array for prefix, single variable for suffix"

    - title: Off-by-One in Product Accumulation
      description: |
        The key insight: update `answer[i]` **before** multiplying `nums[i]` into the running product. This ensures `nums[i]` itself is excluded from the product at position i.

        If you multiply first, you'll include `nums[i]` in its own product.
      wrong_approach: "left_product *= nums[i]; answer[i] = left_product"
      correct_approach: "answer[i] = left_product; left_product *= nums[i]"

  key_takeaways:
    - "**Prefix/suffix products**: A powerful pattern for 'exclude current element' problems"
    - "**Two-pass technique**: Build partial results in one direction, complete them in the other"
    - "**Space optimisation**: The output array can store intermediate results, achieving O(1) extra space"
    - "**No division needed**: This approach naturally handles zeros without special cases"

  time_complexity: "O(n). Two passes through the array, each doing O(1) work per element."
  space_complexity: "O(1). Only two variables (`left_product` and `right_product`) beyond the output array. The output array doesn't count as extra space per the problem statement."

solutions:
  - approach_name: Two-Pass with O(1) Space
    is_optimal: true
    code: |
      def product_except_self(nums: list[int]) -> list[int]:
          n = len(nums)
          answer = [1] * n

          # Left pass: answer[i] = product of all elements to the LEFT of i
          left_product = 1
          for i in range(n):
              answer[i] = left_product
              left_product *= nums[i]  # Include nums[i] for positions after i

          # Right pass: multiply by product of all elements to the RIGHT of i
          right_product = 1
          for i in range(n - 1, -1, -1):
              answer[i] *= right_product
              right_product *= nums[i]  # Include nums[i] for positions before i

          return answer
    explanation: |
      **Time Complexity:** O(n) — Two linear passes.

      **Space Complexity:** O(1) — Only two extra variables (output array doesn't count).

      First pass stores prefix products in the answer array. Second pass multiplies each position by the suffix product. The result is the product of all elements except the current one.

  - approach_name: Separate Prefix/Suffix Arrays
    is_optimal: false
    code: |
      def product_except_self(nums: list[int]) -> list[int]:
          n = len(nums)

          # Build prefix products: prefix[i] = product of nums[0..i-1]
          prefix = [1] * n
          for i in range(1, n):
              prefix[i] = prefix[i - 1] * nums[i - 1]

          # Build suffix products: suffix[i] = product of nums[i+1..n-1]
          suffix = [1] * n
          for i in range(n - 2, -1, -1):
              suffix[i] = suffix[i + 1] * nums[i + 1]

          # Combine: answer[i] = prefix[i] * suffix[i]
          return [prefix[i] * suffix[i] for i in range(n)]
    explanation: |
      **Time Complexity:** O(n) — Three linear passes.

      **Space Complexity:** O(n) — Two extra arrays for prefix and suffix products.

      This version is easier to understand. We compute prefix and suffix products separately, then combine them. The optimal version merges these steps to save space.
