Prefix and Suffix Products: Solving 'Product of Array Except Self'

Hero image for Prefix and Suffix Products: Solving 'Product of Array Except Self'. Image by Robert Stump.
Hero image for 'Prefix and Suffix Products: Solving 'Product of Array Except Self'.' Image by Robert Stump.

Product of Array Except Self is one of those problems that looks almost too simple at first. We are given an array of numbers, and for each position we need the product of every other value except the one sitting at that index.

That usually leads people to one of two first ideas. The first is the bruteforce version: for every index, loop over the whole array again and multiply everything except the current item. That works, but it is quadratic, which is not where the interesting part of the problem sits. The second is to multiply everything once and then divide by each value in turn. That feels clever until zero turns up, at which point the idea starts falling apart quickly.

That is why this is really a prefixandsuffix problem. The clean solution comes from noticing that each answer is simply:

  • everything to the left of the index
  • multiplied by everything to the right of the index

What the Problem is Really Asking

If the input is [1, 2, 3, 4], the output should be [24, 12, 8, 6].

At index 2, for example, we do not need anything mystical. We just need the product of the values before 3 and the values after 3:

  • left side: 1 * 2 = 2
  • right side: 4
  • result: 2 * 4 = 8

The same logic applies at every position. Once we frame the problem like that, division stops looking central, because each answer is really a combination of two partial products.


Why the Division Approach is the Wrong Mental Model

It is worth slowing down here, because the division version is tempting for a reason. If we know the total product of the array, dividing by the current value sounds like the quickest route.

The trouble is not only that the prompt usually forbids division. The deeper issue is that division hides the cases we actually need to understand:

  • if the array contains one zero, almost every answer becomes 0
  • if it contains two zeros, every answer becomes 0
  • if we are relying on division, we end up reasoning about special cases instead of the core structure

Prefix and suffix products avoid all of that. They solve the real problem directly instead of taking a shortcut that immediately needs exceptions.


The Prefix and Suffix Idea

For each index, we want:

  • the product of everything before it
  • the product of everything after it

We can build that in two passes without extra left and right arrays.

On the first pass, we store the prefix product seen so far at each position. On the second pass, we walk from right to left with a running suffix product and multiply it into the existing result.

That means the output array itself becomes our working storage.


A Practical TypeScript Solution

export const productExceptSelf = (values: number[]): number[] => {  const result = new Array<number>(values.length).fill(1);  let prefixProduct = 1;  for (let index = 0; index < values.length; index += 1) {    result[index] = prefixProduct;    prefixProduct *= values[index];  }  let suffixProduct = 1;  for (let index = values.length - 1; index >= 0; index -= 1) {    result[index] *= suffixProduct;    suffixProduct *= values[index];  }  return result;};

What is Happening in Each Pass

The first loop says: "before I include the current value, what is the product of everything to the left?" That is exactly what each slot needs from the prefix side.

For [1, 2, 3, 4], the result array after the first pass becomes:

[1, 1, 2, 6]

That means:

  • index 0 has no values to its left, so it gets 1
  • index 1 has 1 to its left
  • index 2 has 1 * 2 = 2 to its left
  • index 3 has 1 * 2 * 3 = 6 to its left

Then the second pass folds in the suffix side:

  • from the right, index 3 gets 1
  • index 2 gets multiplied by 4
  • index 1 gets multiplied by 3 * 4
  • index 0 gets multiplied by 2 * 3 * 4

That is the point where the full answers appear.


Why This Solution is Better than Storing Two Extra Arrays

We could absolutely build:

  • a leftProducts array
  • a rightProducts array

and combine them afterwards. That is a valid stepping stone, and if you are deriving the idea from scratch it can be a useful one.

The tighter version above is better because it keeps the state smaller without making the reasoning harder. We still do two clean linear passes, but we let the output array carry the prefix information so we do not duplicate storage unnecessarily.

That is the kind of refinement worth making after the main idea is already clear.


Common Mistakes

Updating the Running Product Too Early

If we multiply prefixProduct before storing it in result[index], we accidentally include the current value in the answer. The order matters.

Treating 1 as a strange special case

It is not a hack that emptyleft or emptyright products start at 1. Multiplication needs an identity value, and 1 is exactly that. It is the multiplicative equivalent of starting a sum at 0.

Reaching for Division Because It Looks Shorter

Shorter code is not automatically clearer code. Here, division creates more edgecase reasoning than it removes.


Complexity and Why It Holds up Well

This solution runs in O(n) time because we walk the array twice, and it uses O(1) extra space if we do not count the required output array.

That space detail is usually the point of the exercise. The algorithm is not only linear, it is also disciplined about the state it keeps.


Wrapping up

Product of Array Except Self is a very good reminder that many array problems become easier once we stop thinking in terms of total answers and start thinking in terms of what each position actually needs. Here, each index needs a left product and a right product. That is all.

Key Takeaways

  • The clean solution is based on leftside and rightside products, not on division.
  • Two linear passes are enough to build every answer.
  • The output array can store prefix information so we avoid extra arrays.

Once that prefixandsuffix pattern clicks, the problem stops feeling like a trick question and starts feeling like a straightforward consequence of the data each index needs.


Categories:

  1. Algorithms
  2. Development
  3. Guides
  4. JavaScript
  5. LeetCode
  6. TypeScript