coding interview questions

Prefix and suffix products beat the division trick

The question sounds trivial until the interviewer adds the two constraints that make it interesting: no division, and O(n) time. Given nums = [1, 2, 3, 4], you return [24, 12, 8, 6], where each slot holds the product of every element except the one sitting there. Meta, Amazon, and Microsoft have all rotated this one through phone screens for years, usually as the warm-up before something harder.

Most people reach for division first, so start there and see why the interviewer takes it away.

The division shortcut, and why interviewers kill it

Multiply the whole array into one number, then divide that total by each element. One pass to build the product, one pass to fill the answer.

total = 1
for x in nums:
    total *= x
return [total // x for x in nums]

Clean, fast, and broken the moment a zero shows up. Dividing by zero throws, and even if you guard against it, a single zero means every slot is zero except the zero’s own position, while two zeros make the entire answer zeros. You end up tracking how many zeros you saw and branching on the count, which is exactly the kind of fiddly code the constraint exists to rule out. The point of the no-division rule is to see whether you can find the structure in the problem rather than lean on arithmetic.

Before the good solution, say the brute-force one out loud: for each index, loop the array again and multiply everything else. That’s O(n squared), which is fine at n = 4 and a disaster at n = 100000. Interviewers accept it as a first sentence and then wait for better. Name it, then move on.

Every answer is a left product times a right product

Here is the observation the whole problem is built around. The product of everything except index i equals the product of everything to its left multiplied by the product of everything to its right. Cut the array at i, multiply the two sides, and you never touch nums[i] at all.

Take [1, 2, 3, 4]. For index 2 (the value 3), the left product is 1 * 2 = 2 and the right product is 4, so the answer is 8. Work that out for every index:

index value left product right product answer
0 1 1 24 24
1 2 1 12 12
2 3 2 4 8
3 4 6 1 6

The left product for index 0 is 1 because there is nothing to its left, and the right product at the last index is 1 for the same reason. That empty-product-is-one convention is what makes the two edges fall out on their own instead of each needing a special branch. Miss it and you will write an off-by-one guard for the first and last elements that you did not need.

Two passes, two arrays

Build a prefix array in a left-to-right sweep, build a suffix array in a right-to-left sweep, then multiply them position by position.

def product_except_self(nums):
    n = len(nums)
    prefix = [1] * n
    suffix = [1] * n
    for i in range(1, n):
        prefix[i] = prefix[i - 1] * nums[i - 1]
    for i in range(n - 2, -1, -1):
        suffix[i] = suffix[i + 1] * nums[i + 1]
    return [prefix[i] * suffix[i] for i in range(n)]

Linear time, no division, easy to reason about. The only real cost is the two extra arrays, and that is precisely where the follow-up lands.

Dropping to O(1) extra space

The standard follow-up is to do it without the prefix and suffix arrays. The output array does not count against you, so the trick is to write the prefix products straight into the output on the first pass, then walk backward with a single running variable holding the suffix product and multiply it in as you go.

def product_except_self(nums):
    n = len(nums)
    res = [1] * n
    for i in range(1, n):
        res[i] = res[i - 1] * nums[i - 1]
    right = 1
    for i in range(n - 1, -1, -1):
        res[i] *= right
        right *= nums[i]
    return res

After the first loop, res[i] holds the product of everything to the left of i. The second loop keeps the running product of everything to the right in one scalar, multiplies it into each slot, then folds the current element into that scalar for the next step down. One array, one variable, two passes. This is the version to write once you have shown the interviewer you understand the two-array form, because jumping straight to the clever one without explaining the structure tends to read as memorized.

The zeros and overflow that trip candidates

Zeros need no attention at all, which surprises people who bled on the division version. Because you never divide, a single zero simply makes every prefix or suffix that crosses it become zero, and the arithmetic settles where it should: the zero’s own slot receives the product of the non-zero elements, and every other slot gets zero. Two or more zeros and the whole answer is zero. No counting, no branch. That is the quiet payoff of the prefix-suffix structure over the division trick.

The trap that actually bites is integer overflow, and it depends on your language. In Python you get arbitrary-precision integers and can forget about it. In Java or C++, the product of a long run of large values races past a 32-bit int, so reach for long and, if the interviewer keeps pushing, ask about the value range so you can pick the type deliberately. LeetCode promises the final answer fits in a 32-bit integer for its test data, but the intermediate total in a naive whole-array multiply might not, which is one more mark against the division approach that has to form the full product in the first place.

What gets asked after you nail it

Once the O(1) solution is on the board, the extensions come fast. A common one: if division were allowed and the array had no zeros, how much simpler does it get? The answer is the one-liner from the top, and now you can explain the tradeoff you skipped rather than looking like you never considered it. Another: return each product-except-self modulo a large prime, which changes nothing structurally but checks that you know modular arithmetic distributes over multiplication, so you can take the mod inside the loop instead of at the end.

The real reason to learn this cold is that the shape generalizes. Range-product queries, prefix sums for subarray-sum problems, and the running-max bookkeeping in stock-trading questions all ride the same left-pass-then-right-pass skeleton. If you want to drill it, LeetCode 238 is the canonical version, and Trapping Rain Water (LeetCode 42) is the same prefix-suffix idea wearing a different costume: max-height-from-the-left meeting max-height-from-the-right at each index, water level instead of product. Solve one and you have most of the other.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

1972 Soviet postage stamp commemorating the Mars 2 probe

worth a read

Mars For The Rest of Us — a weekly-or-more deep dive on the technical side of Mars exploration: rocket propulsion, microbiology, mission architecture, and everything in between. Written by Maciej Ceglowski.

Read it on Substack →
Scroll to Top