You're solving Product of Array Except Self. You think: "Easy, just divide total product by each element." You submit.
Wrong Answer.
The test case has a zero in the array, and your solution crashes or gives incorrect results.
Sound familiar? Prefix product has unique challenges that don't exist in prefix sum. Zeros break division, and handling them correctly requires a different approach.
In this guide, you'll learn why zeros are problematic, the prefix/suffix technique, and how to handle all edge cases.
TL;DR
Common pitfalls:
- Using division (breaks with zeros)
- Not handling multiple zeros
- Forgetting negative numbers affect sign
Correct approach:
# Use prefix and suffix products (no division)
result = [1] * n
# Prefix pass
prefix = 1
for i in range(n):
result[i] = prefix
prefix *= nums[i]
# Suffix pass
suffix = 1
for i in range(n-1, -1, -1):
result[i] *= suffix
suffix *= nums[i]The Problem: Zeros Break Division
Naive Approach (WRONG)
def productExceptSelf(nums):
# Calculate total product
total = 1
for num in nums:
total *= num
# Divide by each element
return [total // num for num in nums]Why It Fails
Test case 1: Single zero
nums = [1, 2, 0, 4]
total = 1 * 2 * 0 * 4 = 0
result = [0//1, 0//2, 0//0, 0//4]
= [0, 0, ???, 0] # Division by zero!Test case 2: Multiple zeros
nums = [0, 0, 1]
total = 0
result = [0//0, 0//0, 0//1] # Multiple divisions by zero!The Fundamental Issue
Division assumes you can "undo" multiplication. But you can't divide by zero, so this approach fundamentally breaks.
The Solution: Prefix and Suffix Products
The Approach
Instead of dividing, build products from both directions:
- Prefix pass: Product of all elements to the left
- Suffix pass: Product of all elements to the right
- Multiply them: result[i] = prefix[i] * suffix[i]
The Template
def productExceptSelf(nums: List[int]) -> List[int]:
"""
LeetCode #238: Product of Array Except Self
Time: O(n), Space: O(1) excluding output
"""
n = len(nums)
result = [1] * n
# Build prefix products (left to right)
prefix = 1
for i in range(n):
result[i] = prefix # Product of elements to the left
prefix *= nums[i]
# Build suffix products (right to left) and multiply
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix # Multiply by product of elements to the right
suffix *= nums[i]
return resultWhy It Works
For each position i:
prefix= product of nums[0] * nums[1] * ... * nums[i-1]suffix= product of nums[i+1] * nums[i+2] * ... * nums[n-1]result[i] = prefix * suffix= product of all except nums[i]
No division needed!
Edge Case 1: Single Zero
Example
nums = [1, 2, 0, 4]Trace
Prefix pass:
i=0: result[0] = 1, prefix = 1*1 = 1
i=1: result[1] = 1, prefix = 1*2 = 2
i=2: result[2] = 2, prefix = 2*0 = 0
i=3: result[3] = 0, prefix = 0*4 = 0
result = [1, 1, 2, 0]Suffix pass:
i=3: result[3] *= 1 = 0*1 = 0, suffix = 1*4 = 4
i=2: result[2] *= 4 = 2*4 = 8, suffix = 4*0 = 0
i=1: result[1] *= 0 = 1*0 = 0, suffix = 0*2 = 0
i=0: result[0] *= 0 = 1*0 = 0, suffix = 0*1 = 0
result = [0, 0, 8, 0]Verify:
result[0] = 2*0*4 = 0 ✓
result[1] = 1*0*4 = 0 ✓
result[2] = 1*2*4 = 8 ✓ (only non-zero!)
result[3] = 1*2*0 = 0 ✓Key insight: Only the position with zero has a non-zero product (product of all other elements).
Edge Case 2: Multiple Zeros
Example
nums = [0, 0, 1, 2]Trace
Prefix pass:
i=0: result[0] = 1, prefix = 1*0 = 0
i=1: result[1] = 0, prefix = 0*0 = 0
i=2: result[2] = 0, prefix = 0*1 = 0
i=3: result[3] = 0, prefix = 0*2 = 0
result = [1, 0, 0, 0]Suffix pass:
i=3: result[3] *= 1 = 0*1 = 0, suffix = 1*2 = 2
i=2: result[2] *= 2 = 0*2 = 0, suffix = 2*1 = 2
i=1: result[1] *= 2 = 0*2 = 0, suffix = 2*0 = 0
i=0: result[0] *= 0 = 1*0 = 0, suffix = 0*0 = 0
result = [0, 0, 0, 0]Verify:
result[0] = 0*1*2 = 0 ✓
result[1] = 0*1*2 = 0 ✓
result[2] = 0*0*2 = 0 ✓
result[3] = 0*0*1 = 0 ✓Key insight: With multiple zeros, all products are zero (every position includes at least one zero).
Edge Case 3: Negative Numbers
Example
nums = [-1, 1, 0, -3, 3]Trace
Prefix pass:
result = [1, -1, -1, 0, 0]Suffix pass:
result = [0, 0, 9, 0, 0]Verify:
result[0] = 1*0*(-3)*3 = 0 ✓
result[1] = (-1)*0*(-3)*3 = 0 ✓
result[2] = (-1)*1*(-3)*3 = 9 ✓
result[3] = (-1)*1*0*3 = 0 ✓
result[4] = (-1)*1*0*(-3) = 0 ✓Key insight: Negatives work naturally—no special handling needed.
Edge Case 4: All Zeros
Example
nums = [0, 0, 0]Result
result = [0, 0, 0]All products are zero (every position includes at least one zero).
Edge Case 5: No Zeros
Example
nums = [1, 2, 3, 4]Trace
Prefix pass:
result = [1, 1, 2, 6]Suffix pass:
result = [24, 12, 8, 6]Verify:
result[0] = 2*3*4 = 24 ✓
result[1] = 1*3*4 = 12 ✓
result[2] = 1*2*4 = 8 ✓
result[3] = 1*2*3 = 6 ✓Works perfectly!
Alternative: Count Zeros
When to Use
If you're allowed to use division and just need to handle zeros specially.
The Approach
def productExceptSelf(nums):
zero_count = nums.count(0)
# More than one zero: all products are 0
if zero_count > 1:
return [0] * len(nums)
# Exactly one zero
if zero_count == 1:
# Product of all non-zero elements
product = 1
for num in nums:
if num != 0:
product *= num
# Only the zero position gets the product
return [0 if num != 0 else product for num in nums]
# No zeros: use division
total = 1
for num in nums:
total *= num
return [total // num for num in nums]Why This Works
Case 1: Multiple zeros → Every product includes at least one zero → all zeros
Case 2: One zero → Only the zero position excludes the zero → only it's non-zero
Case 3: No zeros → Safe to use division
Drawbacks
- More complex logic
- Uses division (some problems forbid it)
- Doesn't teach the prefix/suffix pattern
Recommendation: Use prefix/suffix approach—it's cleaner and more general.
Common Mistakes
Mistake 1: Using Division
❌ Wrong:
total = 1
for num in nums:
total *= num
return [total // num for num in nums] # Breaks with zeros✅ Correct:
# Use prefix/suffix (no division)Mistake 2: Not Handling Multiple Zeros
❌ Wrong:
# Only checking for one zero
if 0 in nums:
# Special case✅ Correct:
# Prefix/suffix handles all cases automaticallyMistake 3: Forgetting to Initialize Result
❌ Wrong:
result = [] # Empty list
result[i] = prefix # IndexError!✅ Correct:
result = [1] * n # Pre-allocateThe Correct Template
def productExceptSelf(nums: List[int]) -> List[int]:
n = len(nums)
result = [1] * n
# Prefix pass: product of elements to the left
prefix = 1
for i in range(n):
result[i] = prefix
prefix *= nums[i]
# Suffix pass: product of elements to the right
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix
suffix *= nums[i]
return resultPractice Problems
- Product of Array Except Self (#238) - Classic problem
- Maximum Product Subarray (#152) - DP variant
- Sign of the Product of an Array (#1822) - Count negatives and zeros
FAQ
Q: Why not just count zeros and handle them specially?
A: You can, but prefix/suffix is cleaner, more general, and doesn't require division.
Q: What if the problem allows division?
A: Still use prefix/suffix—it's more robust and handles all edge cases uniformly.
Q: Does this work with negative numbers?
A: Yes, perfectly. Negatives don't require special handling.
Q: What's the space complexity?
A: O(1) excluding the output array (we use the output array to store prefix products).
Q: Can I do this in one pass?
A: No, you need two passes (prefix and suffix). But it's still O(n) time.
Conclusion
Prefix product requires careful handling of zeros, but the prefix/suffix approach solves all edge cases elegantly.
Key takeaways:
- Don't use division (breaks with zeros)
- Use prefix/suffix products
- No special cases needed for zeros or negatives
- O(n) time, O(1) space
The template:
# Prefix pass
for i in range(n):
result[i] = prefix
prefix *= nums[i]
# Suffix pass
for i in range(n-1, -1, -1):
result[i] *= suffix
suffix *= nums[i]Master this pattern, and you'll handle product problems with confidence. For more, see Prefix XOR and Product and the complete guide.
Next time you see "product except self," remember: prefix/suffix, no division.
Want to Practice LeetCode Smarter?
LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.
Also compatible with Edge, Brave, and Opera
