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)O(n) time and must not use the division operation. Follow-up: can you solve in O(1)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

Try it yourself

idle

Starter code: this editor begins with intentional TODOs. Fill the function, run the embedded tests, then compare your solution with the worked approaches below.

Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

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)O(n)1O(n)O(n)
L3 (outer loop)O(1)O(1)nO(n)O(n)
L5, L7 (inner loop + multiply)O(1)O(1)n² totalO(n2)O(n²) ← dominates
L8 (assign)O(1)O(1)nO(n)O(n)

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

Complexity

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

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

final class Solution {
func productExceptSelf(_ nums: [Int]) -> [Int] {
nums.indices.map { excluded in nums.indices.reduce(1) { product, index in index == excluded ? product : product * nums[index] } }
}
}

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)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)O(n)1 eachO(n)O(n)
L4, L5 (prefix pass)O(1)O(1)n-1O(n)O(n)
L6, L7 (suffix pass)O(1)O(1)n-1O(n)O(n)
L8 (combine)O(1)O(1) per elementnO(n)O(n)

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

Complexity

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

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).
final class Solution {
func productExceptSelf(_ nums: [Int]) -> [Int] {
let count = nums.count; var prefix = Array(repeating: 1, count: count), suffix = prefix
for index in 1..<count { prefix[index] = prefix[index - 1] * nums[index - 1] }
for index in stride(from: count - 2, through: 0, by: -1) { suffix[index] = suffix[index + 1] * nums[index + 1] }
return zip(prefix, suffix).map(*)
}
}

Approach 3: Space-optimized (O(1)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)O(n)1O(n)O(n)
L3, L4 (prefix pass)O(1)O(1)n-1O(n)O(n)
L5 (init suffix)O(1)O(1)1O(1)O(1)
L6, L7, L8 (suffix pass)O(1)O(1)n-1O(n)O(n)

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

Complexity

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

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).
final class Solution {
func productExceptSelf(_ nums: [Int]) -> [Int] {
var result = Array(repeating: 1, count: nums.count), prefix = 1
for index in nums.indices { result[index] = prefix; prefix *= nums[index] }
var suffix = 1
for index in nums.indices.reversed() { result[index] *= suffix; suffix *= nums[index] }
return result
}
}

Summary

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

Division would give an O(n)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
  • Array Scans, linear pass tactics for reading an array once, carrying just enough state, and avoiding nested loops.
  • Prefix Sums, accumulation tactics for answering range-sum and subarray-count questions from differences between checkpoints.