Product of Array Except Self
Each answer is the product of everything to its left times everything to its right.
Approach
Without division, build the answer in two sweeps: a left-to-right pass filling each slot with the running prefix product, then a right-to-left pass multiplying in the running suffix product. The output array doubles as scratch space for O(1) extra memory.
Time complexity
O(n)
Space complexity
O(1) extra
Common mistake
Using division to divide the total product by each element — it breaks when the array contains a zero.
Interactive walkthrough — step through it below, free and without an account
Problem
Given an integer array, create a new array where position i contains the product of every input value except nums[i]. Do not use division, and design the algorithm to run in linear time with only constant extra memory beyond the returned array.
Examples
Input: nums = [1, 2, 3, 4]
Output: [24, 12, 8, 6]
Input: nums = [0, 2, 3, 4]
Output: [24, 0, 0, 0]
Problem
Problem
Given an integer array, create a new array where position `i` contains the product of every input value except `nums[i]`. Do not use division, and design the algorithm to run in linear time with only constant extra memory beyond the returned array.
Problem and Goal
Learning goal: Recognize what each output position must include and why a direct total-product shortcut is unsafe.
Core idea
At index i, the answer must exclude exactly one value: nums[i]. Everything else naturally splits into two independent groups, the values before i and the values after i. Therefore, answer[i] is the product of a left region and a right region.
Algorithm plan
- Read the input as two regions around each index.
- Combine the left product with the right product.
- Avoid division so zeros require no special case.
Micro-example
For [1, 2, 3, 4], index 2 contains 3.
The left product is 1 × 2 = 2; the right product is 4, so the answer is 8.
Pitfalls
- The current element must not be included in its own answer.
- A total-product division approach is invalid when an element is zero.
- Empty left or right regions should contribute the multiplicative identity
1.
Recap
- Each answer is left product × right product.
- The identity product
1handles array boundaries. - No division is needed.
Problem and Goal
Split around the excluded element
For index `i`, compute `answer[i] = (product of nums[0:i]) × (product of nums[i+1:n])`.
Think in two directions: prefix on the left, suffix on the right.
Key Idea: Prefix and Suffix Products
Learning goal: See how every answer can be assembled from two running products.
Core idea
A prefix product summarizes everything encountered from the left. A suffix product summarizes everything encountered from the right. If we place the prefix product into output[i] before multiplying by the suffix product, the same output slot becomes the complete answer.
Key insight
The two passes never need to store all prefixes or suffixes. One running product is enough in each direction.
Algorithm plan
- During the left-to-right pass, write the product strictly before
iintooutput[i]. - During the right-to-left pass, multiply
output[i]by the product strictly afteri. - Update the running product only after using the current slot.
Micro-example
For [1, 2, 3, 4], the first pass writes [1, 1, 2, 6].
The reverse pass multiplies by suffix products 1, 4, 12, 24 in reverse position order.
Pitfalls
- Updating
prefixbefore writing would includenums[i]incorrectly. - Updating
suffixbefore multiplying would includenums[i]incorrectly. - The output array is allowed to be scratch space; a separate prefix array is unnecessary.
Recap
- Left sweep supplies the prefix side.
- Right sweep supplies the suffix side.
- The output array stores intermediate values safely.
Key Idea: Prefix and Suffix Products
Before processing index `i`, `prefix` equals the product of all values strictly left of `i`.
At `i = 2`, `prefix = 1 × 2 = 2`.
Main Approach: Two Sweeps
Learning goal: Translate the prefix/suffix idea into a correct linear-time algorithm.
Core idea
Initialize prefix and suffix to 1, the identity for multiplication. The first sweep fills output[i] with the product of all earlier values, then extends prefix with nums[i]. The second sweep runs backward, multiplies the current slot by suffix, and only then extends suffix with nums[i].
Algorithm plan
- Create
outputwith the same length asnums. - Set
prefix = 1. - For
ifrom left to right, writeoutput[i] = prefix, then setprefix *= nums[i]. - Set
suffix = 1. - For
ifrom right to left, setoutput[i] *= suffix, thensuffix *= nums[i]. - Return
output.
prefix = 1
for i in range(n):
output[i] = prefix
prefix *= nums[i]
suffix = 1
for i in range(n - 1, -1, -1):
output[i] *= suffix
suffix *= nums[i]Micro-example
At index 3, the left pass stores 1 × 2 × 3 = 6.
The reverse pass uses an empty right region, so it multiplies by 1; the answer remains 6.
Pitfalls
- The reverse loop must include index
0. - Use the current value after writing or multiplying the slot.
- Do not allocate prefix and suffix arrays if O(1) extra space is required.
Recap
- Two passes cover both sides of every index.
- Each pass performs constant work per element.
- The returned array doubles as scratch space.
Main Approach: Two Sweeps
| Approach | Time | Extra space | Main issue |
|---|---|---|---|
| Two sweeps | O(n) | O(1) | Uses output as scratch space |
| Division by total product | O(n) | O(1) | Breaks when the array contains zero |
| Nested product loops | O(n^2) | O(1) | Repeats work for every position |
Walkthrough: [1, 2, 3, 4]
Learning goal: Trace both passes and verify every intermediate value on one complete example.
Core idea The left pass establishes a promise for each slot: it contains the product of all values before that index. The right pass preserves that prefix information while multiplying in the product of all values after the index. At the end, each slot has exactly the two regions it needs.
Algorithm plan
- Start with
prefix = 1and scan indices0through3. - Store the current prefix before extending it.
- Start with
suffix = 1and scan indices3through0. - Multiply first, then extend the suffix.
Micro-example
Left pass: [1, 1, 2, 6].
Right pass: index 3 stays 6, index 2 becomes 8, index 1 becomes 12, and index 0 becomes 24.
Why it works
At every reverse step, output[i] already represents the left side, while suffix represents the right side. Their product is the desired answer.
Pitfalls
- At index
0, the left side is empty and contributes1. - At index
3, the right side is empty and contributes1. - Do not confuse the running product with the value currently stored in
output[i].
Recap
- The first pass produces prefix-only values.
- The second pass completes them with suffix products.
- The final array is
[24, 12, 8, 6].
Walkthrough: [1, 2, 3, 4]
End-to-end trace for the example nums = [1, 2, 3, 4].
Pitfalls and Edge Cases
Learning goal: Explain why division is the wrong shortcut and verify that the two-pass method handles zeros.
Core idea
Dividing the total product by nums[i] appears short, but it fails immediately when nums[i] is zero. Even special-casing one zero becomes awkward with multiple zeros. The two-sweep method never divides and simply lets zero flow into every product that should contain it.
Algorithm plan
- Use
1for an empty side of the array. - Write or multiply before updating the running product.
- Treat negative values exactly like positive values; multiplication already handles the sign.
- Test arrays containing zero, multiple zeros, and negative numbers.
Micro-example
For [0, 2, 3, 4], index 0 gets 2 × 3 × 4 = 24.
Every other index has the zero on one side, so its answer is 0.
Common pitfall
Using total_product // nums[i] breaks when nums[i] = 0; the total product is zero and division by zero is undefined.
Pitfalls
- A zero must not be “removed” through division.
- Multiple zeros should produce an all-zero result.
- Negative values can make the result negative, but require no algorithm change.
Recap
- No division means zero handling is automatic.
- Empty sides contribute
1. - Update running products after using the current index.
Pitfalls and Edge Cases
Why should we avoid dividing the total product by each `nums[i]`?
Complexity and Wrap-Up
Learning goal: Justify the target complexity and retain the core invariant for implementation.
Core idea
The algorithm visits every element once from the left and once from the right. Each visit performs a constant number of multiplications and assignments. Apart from the returned output array, it stores only prefix, suffix, and a loop index.
Algorithm plan
- Allocate the required output array of length
n. - Count the left sweep as
O(n). - Count the right sweep as
O(n). - Treat the output array as the required result, not as extra working memory.
Micro-example
For n = 4, there are four prefix writes and four suffix multiplications.
The work grows proportionally with n, rather than comparing every pair of positions.
Why it meets the target
Two linear passes add to O(n), while the only auxiliary variables are constant-sized scalars.
Pitfalls
- Do not count the returned output array as O(n) extra space for this problem's convention.
- A separate prefix or suffix array would raise auxiliary space to O(n).
- Nested loops would lose the linear-time guarantee.
Recap
- Time:
O(n). - Extra space:
O(1)beyond the output array. - Invariant: each slot becomes left product × right product.
Complexity and Wrap-Up
Break down the cost of the two-sweep solution.
Now try it on your own problem
Paste any LeetCode problem — or one of your own — and codebrew builds a walkthrough like this one for it.
Build a lesson from any problem
