Skip to content

238. Product of Array Except Self (Medium)

Problem

Given an integer array nums, return an array answer such that answer[i] equals the product of all elements of nums except nums[i].

The algorithm must run in O(n) time and must not use the division operation. Follow-up: can you solve in O(1) extra space (the output array doesn’t count)?

Example

  • nums = [1, 2, 3, 4][24, 12, 8, 6]
  • nums = [-1, 1, 0, -3, 3][0, 0, 9, 0, 0]

LeetCode 238 · Link · Medium

Approach 1: Brute force, nested product

For each i, multiply all other elements.

def product_except_self(nums: list[int]) -> list[int]:
n = len(nums) # L1: O(1)
answer = [0] * n # L2: O(n)
for i in range(n): # L3: outer loop, n iterations
prod = 1 # L4: O(1) reset
for j in range(n): # L5: inner loop, n iterations
if j != i: # L6: O(1) guard
prod *= nums[j] # L7: O(1) multiply
answer[i] = prod # L8: O(1) assign
return answer

Where the time goes, line by line

Variables: n = len(nums).

LinePer-call costTimes executedContribution
L2 (init output)O(n)1O(n)
L3 (outer loop)O(1)nO(n)
L5, L7 (inner loop + multiply)O(1)n² totalO(n²) ← dominates
L8 (assign)O(1)nO(n)

The nested multiply gives exactly n*(n-1) multiplications.

Complexity

  • Time: O(n²), driven by L5/L7 (nested loop multiplications).
  • Space: O(1) excluding the output.

Too slow for the problem’s stated constraint but useful as a sanity check.

Approach 2: Prefix and suffix products (two auxiliary arrays)

answer[i] = (product of everything left of i) × (product of everything right of i).

Compute both in O(n) and combine.

def product_except_self(nums: list[int]) -> list[int]:
n = len(nums) # L1: O(1)
prefix = [1] * n # L2: O(n)
suffix = [1] * n # L3: O(n)
for i in range(1, n): # L4: forward pass, n-1 steps
prefix[i] = prefix[i - 1] * nums[i - 1] # L5: O(1)
for i in range(n - 2, -1, -1): # L6: backward pass, n-1 steps
suffix[i] = suffix[i + 1] * nums[i + 1] # L7: O(1)
return [prefix[i] * suffix[i] for i in range(n)] # L8: O(n)

Where the time goes, line by line

Variables: n = len(nums).

LinePer-call costTimes executedContribution
L2, L3 (init arrays)O(n)1 eachO(n)
L4, L5 (prefix pass)O(1)n-1O(n)
L6, L7 (suffix pass)O(1)n-1O(n)
L8 (combine)O(1) per elementnO(n)

Three linear passes, each O(n). All three contribute equally; none dominates asymptotically.

Complexity

  • Time: O(n), driven by the three linear passes (L4/L5, L6/L7, L8).
  • Space: O(n) for the two auxiliary arrays.

Approach 3: Space-optimized (O(1) extra space)

Reuse the output array for the prefix pass; track the suffix product as a single running scalar on a second pass.

def product_except_self(nums: list[int]) -> list[int]:
n = len(nums) # L1: O(1)
answer = [1] * n # L2: O(n) output array
# First pass: answer[i] = product of all elements left of i
for i in range(1, n): # L3: n-1 steps
answer[i] = answer[i - 1] * nums[i - 1] # L4: O(1)
# Second pass: multiply by product of all elements right of i
suffix = 1 # L5: O(1) running scalar
for i in range(n - 1, -1, -1): # L6: n steps, right to left
answer[i] *= suffix # L7: O(1) multiply-in-place
suffix *= nums[i] # L8: O(1) extend suffix product
return answer

Where the time goes, line by line

Variables: n = len(nums).

LinePer-call costTimes executedContribution
L2 (init output)O(n)1O(n)
L3, L4 (prefix pass)O(1)n-1O(n)
L5 (init suffix)O(1)1O(1)
L6, L7, L8 (suffix pass)O(1)n-1O(n)

Two linear passes, no auxiliary arrays. The suffix accumulator eliminates the need for a separate suffix[] array.

Complexity

  • Time: O(n), driven by L3/L4 and L6/L7/L8 (two linear passes).
  • Space: O(1) extra (the output array is not counted per the problem).

Summary

ApproachTimeSpace
Nested productO(n²)O(1)
Prefix + suffix arraysO(n)O(n)
Space-optimizedO(n)O(1) extra

Division would give an O(n) single-pass solution, forbidden here because of how it handles zeros (and to force the prefix/suffix idea, which generalizes to many problems).

Test cases

# Quick smoke tests, paste into a REPL or save as test_product_except_self.py and run.
# Uses the canonical implementation (Approach 3: space-optimized).
def product_except_self(nums: list[int]) -> list[int]:
n = len(nums)
answer = [1] * n
for i in range(1, n):
answer[i] = answer[i - 1] * nums[i - 1]
suffix = 1
for i in range(n - 1, -1, -1):
answer[i] *= suffix
suffix *= nums[i]
return answer
def _run_tests():
assert product_except_self([1, 2, 3, 4]) == [24, 12, 8, 6]
assert product_except_self([-1, 1, 0, -3, 3]) == [0, 0, 9, 0, 0]
assert product_except_self([1, 1]) == [1, 1]
assert product_except_self([2, 3]) == [3, 2]
assert product_except_self([1, 0]) == [0, 1]
print("all tests pass")
if __name__ == "__main__":
_run_tests()
  • Arrays, input and output; classic prefix/suffix-product pattern