Builds on Prefix Sums, which you haven’t finished yet.
Prefix & Suffix Passes (Left-Right Scans)
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.
Prefix-Suffix Scan
array ➔ left-to-right prefix pass + right-to-left suffix pass ➔ answer from both sides of i| 1 | from typing import List |
| 2 | |
| 3 | def product_except_self(nums: List[int]) -> List[int]: |
| 4 | """Product of Array Except Self: one pass from the left, one from the right, no division. |
| 5 | Time: O(N) | Space: O(1) extra, output not counted |
| 6 | """ |
| 7 | n = len(nums) |
| 8 | res = [1] * n # res[i] ends as the product of every element except nums[i] |
| 9 | |
| 10 | # 1. Left-To-Right Pass: |
| 11 | # prefix is always the product of nums[0..i-1] (the empty product is 1), so writing res[i] before folding nums[i] in keeps i out of its own answer. |
| 12 | prefix = 1 |
| 13 | for i in range(n): |
| 14 | res[i] = prefix |
| 15 | prefix *= nums[i] |
| 16 | |
| 17 | # 2. Right-To-Left Pass: |
| 18 | # suffix is always the product of nums[i+1..n-1], multiplied into the left-side product already stored in res[i], so each res[i] ends covering every index except i. |
| 19 | suffix = 1 |
| 20 | for i in range(n - 1, -1, -1): |
| 21 | res[i] *= suffix |
| 22 | suffix *= nums[i] |
| 23 | |
| 24 | return res |
res[i] is written, prefix is the product of nums[0..i-1]; when it is multiplied, suffix is the product of nums[i+1..n-1]. Neither ever contains nums[i].Everything res[i] needs lies either left of i or right of i, and each side is a running total one sweep can carry, so fill res in two passes: left to right with prefix, then right to left with suffix. When the first loop writes res[i] = prefix, prefix is exactly the product of nums[0..i-1]; when the second loop runs res[i] *= suffix, suffix is exactly the product of nums[i+1..n-1]. Together they cover every element except nums[i], with no division, so a zero in nums needs no special case. The trap is the order inside each loop: write into res[i] first and fold nums[i] into prefix (or suffix) after it, because the other order puts nums[i] into its own answer.
See it run
Left and Right Passes, step by step
pass -i -prefix 1suffix 1
The top row is nums. The bottom row is res. Pass 1 fills res from the left. Pass 2 multiplies in the product from the right.
foldthe number multiplied into prefix or suffix after the writeupdatethe answer cell that is written (pass 1) or multiplied (pass 2) at this step
Start: res is [1, 1, 1, 1], prefix = 1 and suffix = 1. Pass 1 goes left to right.
Gotcha 1 of 3
- The trap
Gotcha 1: Folding
nums[i]intoprefixbefore writingres[i]Mistakeprefix *= nums[i] res[i] = prefixFixres[i] = prefix prefix *= nums[i]What happens:
res[i]must get the product of the values left ofi. Folding first multipliesnums[i]intoprefixbefore the write. Thenres[i]includesnums[i]itself. The second loop has the same rule. There,res[i] *= suffixcomes beforesuffix *= nums[i].Example: nums = [1, 2, 3, 4]Mistake: returns [24, 24, 24, 24]Fix: returns [24, 12, 8, 6]
- You count subarrays whose sum hits a target, as in Subarray Sum Equals K (LC 560): each position needs a lookup among earlier prefix sums, not a total of everything on its right. See Prefix Sum Map
- Only the single best pair of positions matters and the shorter end can always be dropped, as in Container With Most Water (LC 11), so there is no answer per position to build from both sides. See Converging Pointers
Deep Dive
Why a prefix total alone is not enough
A prefix total answers a question about numbers on one side of an index. The answer for res[i] here needs numbers on both sides of i, and it must leave out nums[i].
A sum can be fixed by subtraction: the total minus nums[i]. A product would need a division: the total product divided by nums[i]. That fails when nums[i] is 0, and the task forbids division.
So this card does not remove nums[i] from a total. It builds the answer from two parts that never contained nums[i]: the product of everything to the left of i, and the product of everything to the right of i.
Each part is a running product that one sweep carries. A sweep from the left gives the left part for every index. A sweep from the right gives the right part for every index.
Two passes, traced on [1, 2, 3, 4]
Pass 1 runs left to right with prefix = 1. At each i, res[i] = prefix runs first, and prefix *= nums[i] runs second. The writes are 1, 1, 2, 6, so res = [1, 1, 2, 6]. Each value is the product of the numbers to the left.
Pass 2 runs right to left with suffix = 1. At each i, res[i] *= suffix runs first, and suffix *= nums[i] runs second. At i = 3, suffix is 1 and res[3] stays 6. Then suffix is 4.
At i = 2, res[2] becomes 2 * 4 = 8, and suffix is 12. At i = 1, res[1] becomes 1 * 12 = 12, and suffix is 24. At i = 0, res[0] becomes 1 * 24 = 24.
The result is [24, 12, 8, 6]. For index 2, the left part is 1 * 2 = 2, the right part is 4, and the answer is 8, the product of 1, 2 and 4.
A zero needs no special case. For [-1, 1, 0, -3, 3] the same code returns [0, 0, 9, 0, 0]. Only the index of the zero keeps a non-zero product, because no other index leaves the zero out.
Write first, fold second
The order inside each loop is the whole card. When res[i] = prefix runs, prefix must be the product of nums[0..i-1]. That is true only if nums[i] has not been multiplied in yet.
Suppose prefix *= nums[i] runs first. Then res[i] contains nums[i], and res after pass 1 becomes [1, 2, 6, 24] for [1, 2, 3, 4]. The second pass then returns [24, 24, 24, 24], not [24, 12, 8, 6].
Pass 2 has the same rule. res[i] *= suffix must run before suffix *= nums[i], so index i is not in its own right-hand part.
Two smaller points belong with this. Both running products start at 1, the product of no numbers. A start of 0 makes every product 0. And pass 2 must multiply with *=. The statement res[i] = suffix throws away the left part that pass 1 stored.
Cost, and what this card adds to Subtract Prefixes
Each pass touches every index once, so the time is O(N). The output res is not counted as extra space. The two scalars prefix and suffix are the only extra memory, so the space is O(1) extra.
Subtract Prefixes builds a table once and answers a range by subtracting two entries. That needs an operation with an inverse, such as addition. Multiplication has no safe inverse when a value can be 0.
This card keeps the same idea, a running total that is built once, but it keeps one total per direction. Use it whenever the answer at i combines what lies left of i with what lies right of i.
Trapping Rain Water (LC 42) is the same shape. The water above index i depends on the tallest bar to the left and the tallest bar to the right. Each is a running maximum from one direction. The combining step is a minimum of the two instead of a product.
60-Second Pitch
"For each index I need the product of everything except that index. Division by the total product fails on zeros, so I build the answer from two parts: the product to the left of i and the product to the right of i. Pass 1 goes left to right. It writes res[i] = prefix, then multiplies nums[i] into prefix. Pass 2 goes right to left. It does res[i] *= suffix, then multiplies nums[i] into suffix. In both passes the write happens before the multiplication, so an index never sees itself. The time is O(N), and the extra space is O(1) because the output array is not counted. Zeros need no special case."
Common Questions
- Why not divide the total product by nums[i]?
Division fails when
nums[i]is 0, and the total product is 0 when any value is 0. A fix with a count of zeros is possible, but the problem forbids division. Two passes need no special case.- Why is the extra space
O(1)? The output list
resis the answer, so it is not counted as extra space. The code adds onlyprefix,suffixand the loop index. Pass 1 stores the left products inresitself, and pass 2 multiplies the right products into the same list.- What does the card's invariant say about the two loops?
When
res[i]is written,prefixis the product ofnums[0..i-1]. Whenres[i]is multiplied,suffixis the product ofnums[i+1..n-1]. Neither containsnums[i].- How does the same shape solve Trapping Rain Water (LC 42)?
The water above bar
iis the smaller of the tallest bar on the left and the tallest bar on the right, minus the height of bari. A left pass stores the running maximum. A right pass computes the other maximum and sums the result. The running total is a maximum instead of a product.- When is this card the wrong tool?
It is the wrong tool when you need one best pair or one best range and not an answer for every position. In Container With Most Water (LC 11) the shorter end can be dropped, so two pointers are enough. It also does not apply to counting subarrays with a target sum, where Subarray Sum Equals K (LC 560) needs a lookup among earlier prefix sums.
SWE Reasoning
Spot it
- The answer at each position depends on everything to the left and everything to the right of that position (Product of Array Except Self (LC 238), Trapping Rain Water (LC 42)).
- The task excludes the item itself, and division is forbidden or unsafe because a value can be 0.
- Rescanning both sides for every position would cost
O(N^2). - Each side is a running total that one sweep can carry.
The invariant, step by step
resstarts as[1] * n, andprefixstarts at 1, the product of no numbers.- In pass 1, at index
i,res[i] = prefixstores the product ofnums[0..i-1]. - Then
prefix *= nums[i]addsnums[i]for the next index. The write came first, sonums[i]is never in its own answer. - In pass 2,
suffixstarts at 1. At indexi,res[i] *= suffixmultiplies in the product ofnums[i+1..n-1]. - Then
suffix *= nums[i]addsnums[i]for the index to the left. - Each
res[i]now holds the left product times the right product, which is every number exceptnums[i].
Traps
- Fold before write.
prefix *= nums[i]beforeres[i] = prefixputsnums[i]in its own answer.[1, 2, 3, 4]gives[24, 24, 24, 24]. res[i] = suffixin pass 2. It replaces the left part.[1, 2, 3, 4]gives[24, 12, 4, 1].- A start value of 0.
prefix = 0makes every product 0. - Division by the total. It fails when a value is 0, and the problem forbids it.
Practice
Builds on Prefix Sums, which you haven’t finished yet.
Prefix & Suffix Passes (Left-Right Scans)
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.
Prefix-Suffix Scan
array ➔ left-to-right prefix pass + right-to-left suffix pass ➔ answer from both sides of i| 1 | from typing import List |
| 2 | |
| 3 | def product_except_self(nums: List[int]) -> List[int]: |
| 4 | """Product of Array Except Self: one pass from the left, one from the right, no division. |
| 5 | Time: O(N) | Space: O(1) extra, output not counted |
| 6 | """ |
| 7 | n = len(nums) |
| 8 | res = [1] * n # res[i] ends as the product of every element except nums[i] |
| 9 | |
| 10 | # 1. Left-To-Right Pass: |
| 11 | # prefix is always the product of nums[0..i-1] (the empty product is 1), so writing res[i] before folding nums[i] in keeps i out of its own answer. |
| 12 | prefix = 1 |
| 13 | for i in range(n): |
| 14 | res[i] = prefix |
| 15 | prefix *= nums[i] |
| 16 | |
| 17 | # 2. Right-To-Left Pass: |
| 18 | # suffix is always the product of nums[i+1..n-1], multiplied into the left-side product already stored in res[i], so each res[i] ends covering every index except i. |
| 19 | suffix = 1 |
| 20 | for i in range(n - 1, -1, -1): |
| 21 | res[i] *= suffix |
| 22 | suffix *= nums[i] |
| 23 | |
| 24 | return res |
res[i] is written, prefix is the product of nums[0..i-1]; when it is multiplied, suffix is the product of nums[i+1..n-1]. Neither ever contains nums[i].Everything res[i] needs lies either left of i or right of i, and each side is a running total one sweep can carry, so fill res in two passes: left to right with prefix, then right to left with suffix. When the first loop writes res[i] = prefix, prefix is exactly the product of nums[0..i-1]; when the second loop runs res[i] *= suffix, suffix is exactly the product of nums[i+1..n-1]. Together they cover every element except nums[i], with no division, so a zero in nums needs no special case. The trap is the order inside each loop: write into res[i] first and fold nums[i] into prefix (or suffix) after it, because the other order puts nums[i] into its own answer.
See it run
Left and Right Passes, step by step
pass -i -prefix 1suffix 1
The top row is nums. The bottom row is res. Pass 1 fills res from the left. Pass 2 multiplies in the product from the right.
foldthe number multiplied into prefix or suffix after the writeupdatethe answer cell that is written (pass 1) or multiplied (pass 2) at this step
Start: res is [1, 1, 1, 1], prefix = 1 and suffix = 1. Pass 1 goes left to right.
Gotcha 1 of 3
- The trap
Gotcha 1: Folding
nums[i]intoprefixbefore writingres[i]Mistakeprefix *= nums[i] res[i] = prefixFixres[i] = prefix prefix *= nums[i]What happens:
res[i]must get the product of the values left ofi. Folding first multipliesnums[i]intoprefixbefore the write. Thenres[i]includesnums[i]itself. The second loop has the same rule. There,res[i] *= suffixcomes beforesuffix *= nums[i].Example: nums = [1, 2, 3, 4]Mistake: returns [24, 24, 24, 24]Fix: returns [24, 12, 8, 6]
- You count subarrays whose sum hits a target, as in Subarray Sum Equals K (LC 560): each position needs a lookup among earlier prefix sums, not a total of everything on its right. See Prefix Sum Map
- Only the single best pair of positions matters and the shorter end can always be dropped, as in Container With Most Water (LC 11), so there is no answer per position to build from both sides. See Converging Pointers
Deep Dive
Why a prefix total alone is not enough
A prefix total answers a question about numbers on one side of an index. The answer for res[i] here needs numbers on both sides of i, and it must leave out nums[i].
A sum can be fixed by subtraction: the total minus nums[i]. A product would need a division: the total product divided by nums[i]. That fails when nums[i] is 0, and the task forbids division.
So this card does not remove nums[i] from a total. It builds the answer from two parts that never contained nums[i]: the product of everything to the left of i, and the product of everything to the right of i.
Each part is a running product that one sweep carries. A sweep from the left gives the left part for every index. A sweep from the right gives the right part for every index.
Two passes, traced on [1, 2, 3, 4]
Pass 1 runs left to right with prefix = 1. At each i, res[i] = prefix runs first, and prefix *= nums[i] runs second. The writes are 1, 1, 2, 6, so res = [1, 1, 2, 6]. Each value is the product of the numbers to the left.
Pass 2 runs right to left with suffix = 1. At each i, res[i] *= suffix runs first, and suffix *= nums[i] runs second. At i = 3, suffix is 1 and res[3] stays 6. Then suffix is 4.
At i = 2, res[2] becomes 2 * 4 = 8, and suffix is 12. At i = 1, res[1] becomes 1 * 12 = 12, and suffix is 24. At i = 0, res[0] becomes 1 * 24 = 24.
The result is [24, 12, 8, 6]. For index 2, the left part is 1 * 2 = 2, the right part is 4, and the answer is 8, the product of 1, 2 and 4.
A zero needs no special case. For [-1, 1, 0, -3, 3] the same code returns [0, 0, 9, 0, 0]. Only the index of the zero keeps a non-zero product, because no other index leaves the zero out.
Write first, fold second
The order inside each loop is the whole card. When res[i] = prefix runs, prefix must be the product of nums[0..i-1]. That is true only if nums[i] has not been multiplied in yet.
Suppose prefix *= nums[i] runs first. Then res[i] contains nums[i], and res after pass 1 becomes [1, 2, 6, 24] for [1, 2, 3, 4]. The second pass then returns [24, 24, 24, 24], not [24, 12, 8, 6].
Pass 2 has the same rule. res[i] *= suffix must run before suffix *= nums[i], so index i is not in its own right-hand part.
Two smaller points belong with this. Both running products start at 1, the product of no numbers. A start of 0 makes every product 0. And pass 2 must multiply with *=. The statement res[i] = suffix throws away the left part that pass 1 stored.
Cost, and what this card adds to Subtract Prefixes
Each pass touches every index once, so the time is O(N). The output res is not counted as extra space. The two scalars prefix and suffix are the only extra memory, so the space is O(1) extra.
Subtract Prefixes builds a table once and answers a range by subtracting two entries. That needs an operation with an inverse, such as addition. Multiplication has no safe inverse when a value can be 0.
This card keeps the same idea, a running total that is built once, but it keeps one total per direction. Use it whenever the answer at i combines what lies left of i with what lies right of i.
Trapping Rain Water (LC 42) is the same shape. The water above index i depends on the tallest bar to the left and the tallest bar to the right. Each is a running maximum from one direction. The combining step is a minimum of the two instead of a product.
60-Second Pitch
"For each index I need the product of everything except that index. Division by the total product fails on zeros, so I build the answer from two parts: the product to the left of i and the product to the right of i. Pass 1 goes left to right. It writes res[i] = prefix, then multiplies nums[i] into prefix. Pass 2 goes right to left. It does res[i] *= suffix, then multiplies nums[i] into suffix. In both passes the write happens before the multiplication, so an index never sees itself. The time is O(N), and the extra space is O(1) because the output array is not counted. Zeros need no special case."
Common Questions
- Why not divide the total product by nums[i]?
Division fails when
nums[i]is 0, and the total product is 0 when any value is 0. A fix with a count of zeros is possible, but the problem forbids division. Two passes need no special case.- Why is the extra space
O(1)? The output list
resis the answer, so it is not counted as extra space. The code adds onlyprefix,suffixand the loop index. Pass 1 stores the left products inresitself, and pass 2 multiplies the right products into the same list.- What does the card's invariant say about the two loops?
When
res[i]is written,prefixis the product ofnums[0..i-1]. Whenres[i]is multiplied,suffixis the product ofnums[i+1..n-1]. Neither containsnums[i].- How does the same shape solve Trapping Rain Water (LC 42)?
The water above bar
iis the smaller of the tallest bar on the left and the tallest bar on the right, minus the height of bari. A left pass stores the running maximum. A right pass computes the other maximum and sums the result. The running total is a maximum instead of a product.- When is this card the wrong tool?
It is the wrong tool when you need one best pair or one best range and not an answer for every position. In Container With Most Water (LC 11) the shorter end can be dropped, so two pointers are enough. It also does not apply to counting subarrays with a target sum, where Subarray Sum Equals K (LC 560) needs a lookup among earlier prefix sums.
SWE Reasoning
Spot it
- The answer at each position depends on everything to the left and everything to the right of that position (Product of Array Except Self (LC 238), Trapping Rain Water (LC 42)).
- The task excludes the item itself, and division is forbidden or unsafe because a value can be 0.
- Rescanning both sides for every position would cost
O(N^2). - Each side is a running total that one sweep can carry.
The invariant, step by step
resstarts as[1] * n, andprefixstarts at 1, the product of no numbers.- In pass 1, at index
i,res[i] = prefixstores the product ofnums[0..i-1]. - Then
prefix *= nums[i]addsnums[i]for the next index. The write came first, sonums[i]is never in its own answer. - In pass 2,
suffixstarts at 1. At indexi,res[i] *= suffixmultiplies in the product ofnums[i+1..n-1]. - Then
suffix *= nums[i]addsnums[i]for the index to the left. - Each
res[i]now holds the left product times the right product, which is every number exceptnums[i].
Traps
- Fold before write.
prefix *= nums[i]beforeres[i] = prefixputsnums[i]in its own answer.[1, 2, 3, 4]gives[24, 24, 24, 24]. res[i] = suffixin pass 2. It replaces the left part.[1, 2, 3, 4]gives[24, 12, 4, 1].- A start value of 0.
prefix = 0makes every product 0. - Division by the total. It fails when a value is 0, and the problem forbids it.