author avatar BooleanStack

238. Product Of Array Except Self

Master the O(n) prefix-suffix pattern to solve LeetCode 238 without division.

Product of Array Except Self: The Prefix-Suffix Pattern

If you’ve been grinding LeetCode for interviews at Amazon, Apple, or Facebook, you’ve probably run into this one. LeetCode 238 - “Product of Array Except Self” - is one of those problems that looks deceptively simple until you read the constraints.

Here’s what you need to do: take an array nums and create a new array where each element at index i equals the product of all the other elements in the original array. Everything except the element at i.

The catch? You’ve got to do this in O(n) time, and—here’s the kicker—you can’t use division.

Yeah, I know. That second constraint changes everything.

Real-World Analogy

Analogy: The Secret Gift Pool

Think about this scenario: you and 3 friends are pooling money for a gift. Everyone puts cash in an envelope, but you want to know how much everyone else contributed—without counting your own money.

  1. With Division: Easy. Count all the money, then divide by what you put in.
  2. Without Division: Now it gets interesting. You can’t see the total, so instead you ask the friends on your left what their combined total is, and the friends on your right for theirs. Multiply those two numbers together, and boom—you know what everyone else contributed without ever calculating the total or using division.

This is basically what we’re doing with the array problem.

Why Can’t We Just Use Division?

Look, the obvious solution is to multiply everything together, then divide by each element. Simple, right?

Wrong. And not just because the problem says we can’t.

The Zero Problem

Here’s the thing: if your array contains even one zero, the entire product becomes 0. Now what? You’d need to divide by zero for that element, which is… well, mathematically impossible. Your program crashes, or you end up writing a bunch of messy exception handling code.

Critical Failure: With nums = [1, 2, 0, 4], the total product is 0. When you try to calculate the answer for the zero element, you’re doing 0 / 0. That’s undefined, and your code explodes.

Let me show you what the division approach looks like (and where it falls apart):

Visualizing the Division Approach

Product of Array Except Self - Division Approach

Note: This approach uses division, which is typically not allowed in the problem constraints. This is for educational purposes to understand the concept.

Click "Start Visualization" to begin

Total Product

-

Zero Count

0

Current Phase

idle

Input Array

[0]
[1]
[2]
[3]

Result Array

-
[0]
-
[1]
-
[2]
-
[3]

Division Approach Explanation

  1. Calculate Total Product: Multiply all elements together
  2. Divide for Each Element: result[i] = totalProduct / array[i]
  3. Handle Zeros: Special cases when array contains zeros
  4. Limitation: Division not allowed in the original problem
  5. Limitation: Division can cause precision issues with floating point numbers
  6. Time Complexity: O(n) - one or two passes
  7. Space Complexity: O(1) - only storing total product

The Real Solution: Prefix and Suffix Products

Since division is off the table, we need a different approach. Let’s break down what we’re actually calculating for each position.

For any index i, the answer is:

That’s it. We’re just splitting the problem in two.

Answer[i] = (Product of nums[0...i-1]) Ă— (Product of nums[i+1...n-1])

(If you’re at the start or end of the array, the “empty” side just counts as 1.)

First Attempt: Using Two Arrays (O(n) Space)

The straightforward way to do this is to pre-calculate two arrays:

Then multiply Left[i] * Right[i] for your answer. Done.

Visualizing the Two-Pass Approach

Product of Array Except Self - Left/Right Method

Click "Start Visualization" to begin

Input Array

[0]
[1]
[2]
[3]

Left Products

-
[0]
-
[1]
-
[2]
-
[3]

Right Products

-
[0]
-
[1]
-
[2]
-
[3]

Result (Left Ă— Right)

-
[0]
-
[1]
-
[2]
-
[3]

Algorithm Explanation

  1. Left Products: For each index i, store the product of all elements to its left
  2. Right Products: For each index i, store the product of all elements to its right
  3. Final Result: Multiply left[i] Ă— right[i] to get the product of all elements except array[i]
  4. Time Complexity: O(n) - three passes through the array
  5. Space Complexity: O(n) - for left and right arrays

This works perfectly. But can we do better?

The Optimized Version: O(1) Space

Here’s where it gets clever. We’re using O(n) extra space for those Left and Right arrays. But what if we could do this with just the output array and a couple of variables?

Space Optimization Trick: Store the prefix products directly in the output array, then multiply by a running suffix variable in a second pass. No extra arrays needed.

How It Works

Let’s break this down step by step:

  1. Setup: Create your answer array (same length as input)
  2. First Pass (Left to Right):
    • Walk through the array from left to right
    • At each spot, store the product of everything to the left
    • Start with answer[0] = 1 (nothing to the left of the first element)
  3. Second Pass (Right to Left):
    • Walk backwards through the array
    • Keep a running variable for the suffix product (start at 1)
    • Multiply what’s already in answer[i] (the prefix) by this suffix
    • Update the suffix to include the current number

Visualizing the Optimized Solution

Product of Array Except Self - Space Optimized O(1)

Click "Start Visualization" to begin

Left Running Product

1
Forward pass complete

Right Running Product

1
Waiting for backward pass

Input Array

[0]
[1]
[2]
[3]

Result Array (Building In-Place)

-
[0]
-
[1]
-
[2]
-
[3]

Current Phase

Forward Pass (Building left products)
Backward Pass (Multiplying by right products)

Space Optimized Algorithm

  1. Forward Pass: Build result array with left products using a running variable
  2. Backward Pass: Multiply each result[i] by right products using another running variable
  3. Key Insight: We don't need separate left/right arrays, just running products
  4. Time Complexity: O(n) - two passes through the array
  5. Space Complexity: O(1) - only two variables (excluding output array)

It’s like we’re painting the answer in two coats—first the left products, then we layer the right products on top.

Implementation

Alright, here’s the code for the space-optimized solution. Remember, the problem says the output array doesn’t count toward space complexity (which is nice of them).

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] answer = new int[n];
        
        // Left pass: answer[i] will contain the product of all elements to the left of i
        answer[0] = 1;
        for (int i = 1; i < n; i++) {
            answer[i] = answer[i - 1] * nums[i - 1];
        }
        
        // Right pass: R contains the product of all elements to the right of i
        int R = 1;
        for (int i = n - 1; i >= 0; i--) {
            // Multiply the prefix product (already in answer[i]) by the suffix product R
            answer[i] = answer[i] * R;
            // Update R to include nums[i] for the next iteration
            R *= nums[i];
        }
        
        return answer;
    }
}

Complexity Analysis

Time Complexity: O(N)

We make two passes through the array—one forward, one backward. That’s O(2N), which simplifies to O(N). Linear time, just like the problem asked for.

Space Complexity: O(1)

We’re using the output array, but the problem explicitly says that doesn’t count as extra space. Beyond that, we’ve only got a handful of variables (R, i, n). The earlier version with separate Left and Right arrays would be O(N) space, which is why this optimization matters.

Watch Out For These Gotchas

  1. Forgetting to initialize with 1: If you start your accumulator at 0, the entire product chain becomes zero. Not fun to debug.

  2. Off-by-one errors: Your prefix loop should start at index 1 (since you’re looking back at i-1), and your suffix loop starts at n-1. Get these wrong and you’ll be scratching your head for a while.

  3. The zero cases (this is actually kind of cool):

    • No zeros: Everything works normally
    • One zero: Only the zero’s position gets a non-zero answer (it’s the product of everything else). All other positions are 0
    • Two or more zeros: The entire result is zeros, since every “except self” group includes at least one zero

    The beautiful part? Our algorithm handles all of these automatically. No special if statements needed.

Wrapping Up

This problem is a perfect example of how thinking in terms of “what’s to my left” and “what’s to my right” can crack open array problems that seem impossible at first glance. The prefix/suffix pattern shows up everywhere—you’ll see it again in problems like “Trapping Rain Water” and “Candy.”

Once you get comfortable with this approach, you’ll start spotting opportunities to use it all over the place. And that’s when array problems start feeling less like puzzles and more like patterns you recognize.

Now go ace that interview.

Similar Posts