Product of Array Except Self (LeetCode 238)
You have an integer array nums. Build an output array answer where each answer[i] holds the product of every element of nums other than nums[i] itself.
Every prefix or suffix product you'll ever need to compute is guaranteed to fit inside a 32-bit integer.
Solve it in O(n) time, and without dividing.
Worked Examples
nums = [1,2,3,4][24,12,8,6]nums = [-1,1,0,-3,3][0,0,9,0,0]⚖️Formal Constraints & Bounds
2 <= nums.length <= 105-30 <= nums[i] <= 30The input is generated such that
answer[i]is guaranteed to fit in a 32-bit integer.
Core Insight
Everything except nums[i] is everything left of i times everything right of i. A left pass stores the left product in res[i] and a right pass multiplies in the right product, so no division is ever needed.
Real-World Scenario & Production Applications
Bayesian probability updates (independent evidence combination), graphics rendering light transport equations, and zero-safe arithmetic pipeline processing.
Subproblems & Recurrence Decomposition3 Phases
Walk left to right and store in res[i] the product of everything strictly left of i. Write res[i] first, then fold nums[i] into prefix.
res = [1] * n
prefix = 1
for i in range(n):
res[i] = prefix
prefix *= nums[i]Step-by-Step Execution Trace Table
| Index | nums[i] | Prefix Product Pass | Suffix Multiplier | Final res[i] |
|---|---|---|---|---|
| 0 | 1 | 1 | 24 | 24 |
| 1 | 2 | 1 | 12 | 12 |
| 2 | 3 | 2 | 4 | 8 |
| 3 | 4 | 6 | 1 | 6 |
The Math, Step by Step
1. The number the code builds for each index
For each index , the answer is the product of every element except nums[i]. Split those elements into two groups: the elements to the left of and the elements to the right of .
is the prefix product before , and is the suffix product after . Every element except nums[i] is in exactly one of the two groups, so
2. Each product from the one before it
A product with no factors is , called the empty product. So , because nothing is to the left of index 0. , because nothing is to the right of the last index. Every other value takes one multiplication:
So one pass from left to right builds every , and one pass from right to left builds every .
3. Why no division is needed, and why zeros are safe
A shorter idea is "the product of all elements, divided by nums[i]". It fails when nums[i] is 0, because you cannot divide by 0. The problem also forbids division.
only multiplies. A zero in nums is multiplied into every to its right and every to its left. It is never a factor of its own index's answer. So Example 2, which has a zero, needs no special case.
4. Write first, then multiply
In the left pass, the code writes res[i] = prefix before it runs prefix *= nums[i]. At that moment prefix is still , the product of the elements before . If the two lines were swapped, res[i] would get and include nums[i] itself. The right pass uses the same order: res[i] *= suffix runs before suffix *= nums[i].
The right pass also uses *=, not =. res[i] already holds from the left pass. Multiplying keeps it, so res[i] becomes .
5. Example 1 by hand
nums = [1,2,3,4].
nums[i] | ||||
|---|---|---|---|---|
| 0 | 1 | (empty) | ||
| 1 | 2 | |||
| 2 | 3 | |||
| 3 | 4 | (empty) |
After the left pass, res = [1, 1, 2, 6], the column. The right pass multiplies each entry by the column, [24, 12, 4, 1]. The result [24, 12, 8, 6] is the expected output.
6. Example 2 by hand
nums = [-1,1,0,-3,3].
nums[i] | ||||
|---|---|---|---|---|
| 0 | (empty) | |||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | (empty) |
Only index 2 has the zero outside both groups. Its answer is . Every other index has the zero in or in , so its answer is . The result [0, 0, 9, 0, 0] is the expected output.
7. From the formulas to the code
| Formula | In productExceptSelf |
|---|---|
prefix = 1 | |
res[i] gets | res[i] = prefix |
prefix *= nums[i] | |
suffix = 1 | |
res[i] becomes | res[i] *= suffix |
suffix *= nums[i] |
Each pass takes time. Besides the output array res, the code keeps only prefix and suffix, so it uses extra space.
| 1 | res = [1] * n |
| 2 | prefix = 1 |
| 3 | for i in range(n): |
| 4 | res[i] = prefix |
| 5 | prefix *= nums[i] |
| 6 | suffix = 1 |
| 7 | for i in range(n - 1, -1, -1): |
| 8 | res[i] *= suffix |
| 9 | suffix *= nums[i] |
| 10 | return res |
Conceptual Narrative
🧭 Conceptual Foundation & Pattern Intuition
The obvious shortcut — multiply everything, then divide by nums[i] — is banned, and for good reason: any zero in the array breaks division entirely. The prefix/suffix trick sidesteps division altogether.
👥 The Analogy: Everyone Except You, From Two Directions
"The product of everyone except me" is really just "everyone standing to my left, multiplied by everyone standing to my right." So make two separate passes: walking left to right, hand each person the running product of everyone before them. Then walk right to left, multiplying each person's number by the running product of everyone after them. By the time both passes are done, every person is holding exactly "the product of everyone except themselves" — built entirely from multiplication, no division anywhere.
🔑 The Key Idea in the Code
Each index only ever needs its own left-total and right-total — never the array's overall total — which is exactly what makes skipping division possible.
Folding too early:
prefix *= nums[i]beforeres[i] = prefix(orsuffix *= nums[i]beforeres[i] *= suffix) multipliesnums[i]into its own answer. On[1,2,3,4]the left pass alone already stores[1,2,6,24]instead of[1,1,2,6].Overwriting in the second pass:
res[i] = suffixinstead ofres[i] *= suffixthrows away the left product the first pass stored.Wrong start value:
prefixandsuffixstart at1, the empty product. Starting at0makes every answer0.Dividing a total product:
total // nums[i]is not allowed here, and it breaks as soon asnumscontains a zero. Without division, one zero leaves a single non-zero answer and two zeros make every answer0, with no special case.
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
The statement asks, for every index, for "the product of every element of nums other than nums[i]": each answer needs everything on its left and everything on its right. "Without dividing" rules out total // nums[i], and "O(n) time" rules out multiplying the other N - 1 numbers again for every index.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
Invariant: when res[i] = prefix runs, prefix is the product of nums[0..i-1]; when res[i] *= suffix runs, suffix is the product of nums[i+1..n-1]. Neither ever contains nums[i], so after both passes res[i] is the product of all the other numbers.
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
Keeping two extra arrays: a full
leftarray and a fullrightarray also work, but they costO(N)extra space. The follow-up asks forO(1)extra besides the output, so store the left products inresitself and carry the right side as the single numbersuffix.
The 60-Second Interview Pitch
Say this out loud before you type a single line
"I'll use a Prefix-Suffix Scan. Each answer is the product of everything left of i times everything right of i, and each side is a running product I can carry in one sweep. In the first pass, left to right, I write res[i] = prefix and only then fold nums[i] into prefix, so prefix only ever holds the numbers before i. In the second pass, right to left, I multiply res[i] by suffix and then fold nums[i] into suffix. I never divide, so zeros need no special case: one zero leaves a single non-zero answer, and two zeros make every answer zero. Two linear passes give
O(N)time, and besides the output array I keep only prefix and suffix, soO(1)extra space."
Complexity & Mathematical Proof
O(N)
Look at the code: res = [1] * n writes N slots. for i in range(n) runs N times and does one write (res[i] = prefix) and one multiplication (prefix *= nums[i]). for i in range(n - 1, -1, -1) runs N times and does two multiplications (res[i] *= suffix, suffix *= nums[i]). No loop is nested inside another, so the total is N + 2N + 2N = 5N constant-time steps.
O(1) extra, output not counted
Look at the allocations: res is the answer the problem asks for (N integers, output, not counted). Besides it the code keeps n, prefix, suffix and i: four integers, whatever N is. There is no recursion, so the call stack stays constant.
T(N) = N (fill res) + 2N (left pass) + 2N (right pass) = 5N = O(N)
Derivation Progression
res = [1] * n -> N writes
Every slot starts at 1, the empty product.
for i in range(n): 1 write + 1 multiply -> 2N
res[i] = prefix stores the product of nums[0..i-1]; prefix *= nums[i] then extends it by one element. Each iteration is constant work.
for i in range(n - 1, -1, -1): 2 multiplies -> 2N
res[i] *= suffix multiplies in the product of nums[i+1..n-1]; suffix *= nums[i] then extends it by one element.
N + 2N + 2N = 5N = O(N)
The passes run one after the other, never nested, so their costs add up instead of multiplying.
Variable Definitions
len(nums), the number of elements (the code's n)
Memory Architecture & Bounds
O(1): no recursion
O(1): the integers n, prefix, suffix and i
O(N): res, the answer itself, not counted as extra space
Boundary Best / Worst Cases
: both passes always visit every index
: zeros or negative numbers change no step
Pointer Invariant Transition Progression
Senior SWE Deconstruction & Hardware Caveats
Triggers: "the product of every element other than nums[i]", "without dividing", "O(n) time". Each answer needs everything on its left and everything on its right: a Prefix-Suffix Scan, one pass from each end.
and . Multiplying the other numbers again for every index is up to multiplications; two passes are . Every prefix and suffix product is guaranteed to fit in 32 bits. The two passes read the array front to back, then back to front, so they stream well and stay cache-friendly; the only stored array is res, the output.
At scale the risk is the size of the numbers, not the loop. This problem guarantees every prefix and suffix product fits in 32 bits; outside that guarantee, a running product of many values overflows a fixed-width integer, so production code checks the range or uses a wider type.
Product of Array Except Self (LeetCode 238)
You have an integer array nums. Build an output array answer where each answer[i] holds the product of every element of nums other than nums[i] itself.
Every prefix or suffix product you'll ever need to compute is guaranteed to fit inside a 32-bit integer.
Solve it in O(n) time, and without dividing.
Worked Examples
nums = [1,2,3,4][24,12,8,6]nums = [-1,1,0,-3,3][0,0,9,0,0]⚖️Formal Constraints & Bounds
2 <= nums.length <= 105-30 <= nums[i] <= 30The input is generated such that
answer[i]is guaranteed to fit in a 32-bit integer.
Core Insight
Everything except nums[i] is everything left of i times everything right of i. A left pass stores the left product in res[i] and a right pass multiplies in the right product, so no division is ever needed.
Real-World Scenario & Production Applications
Bayesian probability updates (independent evidence combination), graphics rendering light transport equations, and zero-safe arithmetic pipeline processing.
Subproblems & Recurrence Decomposition3 Phases
Walk left to right and store in res[i] the product of everything strictly left of i. Write res[i] first, then fold nums[i] into prefix.
res = [1] * n
prefix = 1
for i in range(n):
res[i] = prefix
prefix *= nums[i]Step-by-Step Execution Trace Table
| Index | nums[i] | Prefix Product Pass | Suffix Multiplier | Final res[i] |
|---|---|---|---|---|
| 0 | 1 | 1 | 24 | 24 |
| 1 | 2 | 1 | 12 | 12 |
| 2 | 3 | 2 | 4 | 8 |
| 3 | 4 | 6 | 1 | 6 |
The Math, Step by Step
1. The number the code builds for each index
For each index , the answer is the product of every element except nums[i]. Split those elements into two groups: the elements to the left of and the elements to the right of .
is the prefix product before , and is the suffix product after . Every element except nums[i] is in exactly one of the two groups, so
2. Each product from the one before it
A product with no factors is , called the empty product. So , because nothing is to the left of index 0. , because nothing is to the right of the last index. Every other value takes one multiplication:
So one pass from left to right builds every , and one pass from right to left builds every .
3. Why no division is needed, and why zeros are safe
A shorter idea is "the product of all elements, divided by nums[i]". It fails when nums[i] is 0, because you cannot divide by 0. The problem also forbids division.
only multiplies. A zero in nums is multiplied into every to its right and every to its left. It is never a factor of its own index's answer. So Example 2, which has a zero, needs no special case.
4. Write first, then multiply
In the left pass, the code writes res[i] = prefix before it runs prefix *= nums[i]. At that moment prefix is still , the product of the elements before . If the two lines were swapped, res[i] would get and include nums[i] itself. The right pass uses the same order: res[i] *= suffix runs before suffix *= nums[i].
The right pass also uses *=, not =. res[i] already holds from the left pass. Multiplying keeps it, so res[i] becomes .
5. Example 1 by hand
nums = [1,2,3,4].
nums[i] | ||||
|---|---|---|---|---|
| 0 | 1 | (empty) | ||
| 1 | 2 | |||
| 2 | 3 | |||
| 3 | 4 | (empty) |
After the left pass, res = [1, 1, 2, 6], the column. The right pass multiplies each entry by the column, [24, 12, 4, 1]. The result [24, 12, 8, 6] is the expected output.
6. Example 2 by hand
nums = [-1,1,0,-3,3].
nums[i] | ||||
|---|---|---|---|---|
| 0 | (empty) | |||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | (empty) |
Only index 2 has the zero outside both groups. Its answer is . Every other index has the zero in or in , so its answer is . The result [0, 0, 9, 0, 0] is the expected output.
7. From the formulas to the code
| Formula | In productExceptSelf |
|---|---|
prefix = 1 | |
res[i] gets | res[i] = prefix |
prefix *= nums[i] | |
suffix = 1 | |
res[i] becomes | res[i] *= suffix |
suffix *= nums[i] |
Each pass takes time. Besides the output array res, the code keeps only prefix and suffix, so it uses extra space.
| 1 | res = [1] * n |
| 2 | prefix = 1 |
| 3 | for i in range(n): |
| 4 | res[i] = prefix |
| 5 | prefix *= nums[i] |
| 6 | suffix = 1 |
| 7 | for i in range(n - 1, -1, -1): |
| 8 | res[i] *= suffix |
| 9 | suffix *= nums[i] |
| 10 | return res |
Conceptual Narrative
🧭 Conceptual Foundation & Pattern Intuition
The obvious shortcut — multiply everything, then divide by nums[i] — is banned, and for good reason: any zero in the array breaks division entirely. The prefix/suffix trick sidesteps division altogether.
👥 The Analogy: Everyone Except You, From Two Directions
"The product of everyone except me" is really just "everyone standing to my left, multiplied by everyone standing to my right." So make two separate passes: walking left to right, hand each person the running product of everyone before them. Then walk right to left, multiplying each person's number by the running product of everyone after them. By the time both passes are done, every person is holding exactly "the product of everyone except themselves" — built entirely from multiplication, no division anywhere.
🔑 The Key Idea in the Code
Each index only ever needs its own left-total and right-total — never the array's overall total — which is exactly what makes skipping division possible.
Folding too early:
prefix *= nums[i]beforeres[i] = prefix(orsuffix *= nums[i]beforeres[i] *= suffix) multipliesnums[i]into its own answer. On[1,2,3,4]the left pass alone already stores[1,2,6,24]instead of[1,1,2,6].Overwriting in the second pass:
res[i] = suffixinstead ofres[i] *= suffixthrows away the left product the first pass stored.Wrong start value:
prefixandsuffixstart at1, the empty product. Starting at0makes every answer0.Dividing a total product:
total // nums[i]is not allowed here, and it breaks as soon asnumscontains a zero. Without division, one zero leaves a single non-zero answer and two zeros make every answer0, with no special case.
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
The statement asks, for every index, for "the product of every element of nums other than nums[i]": each answer needs everything on its left and everything on its right. "Without dividing" rules out total // nums[i], and "O(n) time" rules out multiplying the other N - 1 numbers again for every index.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
Invariant: when res[i] = prefix runs, prefix is the product of nums[0..i-1]; when res[i] *= suffix runs, suffix is the product of nums[i+1..n-1]. Neither ever contains nums[i], so after both passes res[i] is the product of all the other numbers.
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
Keeping two extra arrays: a full
leftarray and a fullrightarray also work, but they costO(N)extra space. The follow-up asks forO(1)extra besides the output, so store the left products inresitself and carry the right side as the single numbersuffix.
The 60-Second Interview Pitch
Say this out loud before you type a single line
"I'll use a Prefix-Suffix Scan. Each answer is the product of everything left of i times everything right of i, and each side is a running product I can carry in one sweep. In the first pass, left to right, I write res[i] = prefix and only then fold nums[i] into prefix, so prefix only ever holds the numbers before i. In the second pass, right to left, I multiply res[i] by suffix and then fold nums[i] into suffix. I never divide, so zeros need no special case: one zero leaves a single non-zero answer, and two zeros make every answer zero. Two linear passes give
O(N)time, and besides the output array I keep only prefix and suffix, soO(1)extra space."
Complexity & Mathematical Proof
O(N)
Look at the code: res = [1] * n writes N slots. for i in range(n) runs N times and does one write (res[i] = prefix) and one multiplication (prefix *= nums[i]). for i in range(n - 1, -1, -1) runs N times and does two multiplications (res[i] *= suffix, suffix *= nums[i]). No loop is nested inside another, so the total is N + 2N + 2N = 5N constant-time steps.
O(1) extra, output not counted
Look at the allocations: res is the answer the problem asks for (N integers, output, not counted). Besides it the code keeps n, prefix, suffix and i: four integers, whatever N is. There is no recursion, so the call stack stays constant.
T(N) = N (fill res) + 2N (left pass) + 2N (right pass) = 5N = O(N)
Derivation Progression
res = [1] * n -> N writes
Every slot starts at 1, the empty product.
for i in range(n): 1 write + 1 multiply -> 2N
res[i] = prefix stores the product of nums[0..i-1]; prefix *= nums[i] then extends it by one element. Each iteration is constant work.
for i in range(n - 1, -1, -1): 2 multiplies -> 2N
res[i] *= suffix multiplies in the product of nums[i+1..n-1]; suffix *= nums[i] then extends it by one element.
N + 2N + 2N = 5N = O(N)
The passes run one after the other, never nested, so their costs add up instead of multiplying.
Variable Definitions
len(nums), the number of elements (the code's n)
Memory Architecture & Bounds
O(1): no recursion
O(1): the integers n, prefix, suffix and i
O(N): res, the answer itself, not counted as extra space
Boundary Best / Worst Cases
: both passes always visit every index
: zeros or negative numbers change no step
Pointer Invariant Transition Progression
Senior SWE Deconstruction & Hardware Caveats
Triggers: "the product of every element other than nums[i]", "without dividing", "O(n) time". Each answer needs everything on its left and everything on its right: a Prefix-Suffix Scan, one pass from each end.
and . Multiplying the other numbers again for every index is up to multiplications; two passes are . Every prefix and suffix product is guaranteed to fit in 32 bits. The two passes read the array front to back, then back to front, so they stream well and stay cache-friendly; the only stored array is res, the output.
At scale the risk is the size of the numbers, not the loop. This problem guarantees every prefix and suffix product fits in 32 bits; outside that guarantee, a running product of many values overflows a fixed-width integer, so production code checks the range or uses a wider type.