Facebook Pixel

238. Product of Array Except Self

Problem Description

You are given an integer array nums. Your task is to create a new array answer where each element answer[i] contains the product of all elements in nums except for nums[i] itself.

For example, if nums = [1, 2, 3, 4], then answer = [24, 12, 8, 6] because:

  • answer[0] = 2 × 3 × 4 = 24 (product of all except nums[0])
  • answer[1] = 1 × 3 × 4 = 12 (product of all except nums[1])
  • answer[2] = 1 × 2 × 4 = 8 (product of all except nums[2])
  • answer[3] = 1 × 2 × 3 = 6 (product of all except nums[3])

The problem has the following constraints:

  • The algorithm must run in O(n) time complexity
  • You cannot use the division operation
  • The product of any prefix or suffix of the array is guaranteed to fit in a 32-bit integer

The solution uses a two-pass approach. In the first pass from left to right, it calculates the product of all elements to the left of each position. In the second pass from right to left, it multiplies each position by the product of all elements to its right. This way, each position ends up with the product of all elements except itself, achieving the required result without using division and in linear time.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since we can't use division, we need to think about what the product of all elements except nums[i] actually represents. For any position i, this product can be broken down into two parts:

  • The product of all elements to the left of i
  • The product of all elements to the right of i

If we multiply these two parts together, we get exactly what we want - the product of all elements except nums[i].

Let's visualize this with an example. For nums = [1, 2, 3, 4] and position i = 2 (value 3):

  • Elements to the left: [1, 2], product = 1 × 2 = 2
  • Elements to the right: [4], product = 4
  • Final answer for position 2: 2 × 4 = 8

This observation leads us to a two-pass strategy:

First Pass (Left to Right): We accumulate the product of all elements to the left of each position. For position i, we store the product of nums[0] × nums[1] × ... × nums[i-1]. We use a running product variable left that starts at 1 and gets multiplied by each element as we go.

Second Pass (Right to Left): We accumulate the product of all elements to the right and multiply it with what we already have. For position i, we multiply the stored left product by the product of nums[i+1] × nums[i+2] × ... × nums[n-1]. We use another running product variable right that accumulates from the right side.

The beauty of this approach is that we build the answer incrementally without ever needing to calculate the full product and divide. Each position gets exactly the product it needs through these two complementary passes.

Solution Approach

We implement the two-pass approach using two variables left and right to track the running products, and an answer array ans to store the results.

Initialization:

  • Create an answer array ans of size n filled with zeros
  • Initialize left = 1 and right = 1 to represent empty products

First Pass (Left to Right):

for i, x in enumerate(nums):
    ans[i] = left
    left *= x

In this pass, for each position i:

  1. Store the current left product in ans[i] - this represents the product of all elements before position i
  2. Update left by multiplying it with the current element nums[i]

After this pass, ans[i] contains the product of all elements to the left of position i.

Second Pass (Right to Left):

for i in range(n - 1, -1, -1):
    ans[i] *= right
    right *= nums[i]

In this reverse pass, for each position i:

  1. Multiply ans[i] by the current right product - this combines the left product (already in ans[i]) with the product of all elements to the right
  2. Update right by multiplying it with the current element nums[i]

Example Walkthrough: For nums = [1, 2, 3, 4]:

After first pass:

  • ans = [1, 1, 2, 6] (products of elements to the left)
  • left progresses as: 1 → 1 → 2 → 6 → 24

After second pass:

  • ans = [24, 12, 8, 6] (final answer)
  • right progresses as: 1 → 4 → 12 → 24

The algorithm runs in O(n) time with two linear passes and uses O(1) extra space (excluding the output array), satisfying all the problem constraints without using division.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with nums = [2, 3, 4, 5].

Initial Setup:

  • Create ans = [0, 0, 0, 0]
  • Set left = 1 and right = 1

First Pass (Left to Right):

StepIndexCurrent ElementActionleft beforeleft afterans array
102ans[0] = left = 111 × 2 = 2[1, 0, 0, 0]
213ans[1] = left = 222 × 3 = 6[1, 2, 0, 0]
324ans[2] = left = 666 × 4 = 24[1, 2, 6, 0]
435ans[3] = left = 242424 × 5 = 120[1, 2, 6, 24]

After the first pass, each position contains the product of all elements to its left.

Second Pass (Right to Left):

StepIndexCurrent ElementActionright beforeright afterans array
135ans[3] *= right = 24 × 111 × 5 = 5[1, 2, 6, 24]
224ans[2] *= right = 6 × 555 × 4 = 20[1, 2, 30, 24]
313ans[1] *= right = 2 × 202020 × 3 = 60[1, 40, 30, 24]
402ans[0] *= right = 1 × 606060 × 2 = 120[60, 40, 30, 24]

Final Result: ans = [60, 40, 30, 24]

Verification:

  • ans[0] = 60 = 3 × 4 × 5 (all except 2)
  • ans[1] = 40 = 2 × 4 × 5 (all except 3)
  • ans[2] = 30 = 2 × 3 × 5 (all except 4)
  • ans[3] = 24 = 2 × 3 × 4 (all except 5)

Each position now contains the product of all elements except itself, achieved through combining left and right products without division.

Solution Implementation

1class Solution:
2    def productExceptSelf(self, nums: List[int]) -> List[int]:
3        """
4        Calculate the product of all elements except self for each position.
5        Uses two passes to avoid division and achieve O(n) time complexity.
6      
7        Args:
8            nums: List of integers
9          
10        Returns:
11            List where each element is the product of all other elements
12        """
13        n = len(nums)
14      
15        # Initialize result array with zeros
16        result = [0] * n
17      
18        # First pass: calculate products of all elements to the left
19        left_product = 1
20        for i in range(n):
21            # Store the product of all elements before current index
22            result[i] = left_product
23            # Update left_product for next iteration
24            left_product *= nums[i]
25      
26        # Second pass: multiply by products of all elements to the right
27        right_product = 1
28        for i in range(n - 1, -1, -1):
29            # Multiply existing left product by right product
30            result[i] *= right_product
31            # Update right_product for next iteration
32            right_product *= nums[i]
33      
34        return result
35
1class Solution {
2    public int[] productExceptSelf(int[] nums) {
3        int n = nums.length;
4        int[] result = new int[n];
5      
6        // First pass: Calculate products of all elements to the left of each index
7        // For each position i, store the product of all elements before index i
8        int leftProduct = 1;
9        for (int i = 0; i < n; i++) {
10            result[i] = leftProduct;      // Store product of all elements to the left
11            leftProduct *= nums[i];        // Update left product for next iteration
12        }
13      
14        // Second pass: Calculate products of all elements to the right of each index
15        // Multiply each position by the product of all elements after index i
16        int rightProduct = 1;
17        for (int i = n - 1; i >= 0; i--) {
18            result[i] *= rightProduct;     // Multiply by product of all elements to the right
19            rightProduct *= nums[i];       // Update right product for next iteration
20        }
21      
22        return result;
23    }
24}
25
1class Solution {
2public:
3    vector<int> productExceptSelf(vector<int>& nums) {
4        int n = nums.size();
5        vector<int> result(n);
6      
7        // First pass: Calculate prefix products (products of all elements to the left)
8        // For each position i, store the product of all elements before index i
9        int leftProduct = 1;
10        for (int i = 0; i < n; i++) {
11            result[i] = leftProduct;      // Store product of all elements to the left of i
12            leftProduct *= nums[i];        // Update left product for next iteration
13        }
14      
15        // Second pass: Calculate suffix products (products of all elements to the right)
16        // and multiply with the prefix products already stored in result
17        int rightProduct = 1;
18        for (int i = n - 1; i >= 0; i--) {
19            result[i] *= rightProduct;     // Multiply with product of all elements to the right of i
20            rightProduct *= nums[i];       // Update right product for next iteration
21        }
22      
23        return result;
24    }
25};
26
1/**
2 * Calculate the product of all elements in the array except self
3 * @param nums - Input array of numbers
4 * @returns Array where each element is the product of all elements except itself
5 */
6function productExceptSelf(nums: number[]): number[] {
7    const length: number = nums.length;
8    const result: number[] = new Array<number>(length);
9  
10    // First pass: Calculate left products
11    // For each position, store the product of all elements to its left
12    let leftProduct: number = 1;
13    for (let i: number = 0; i < length; i++) {
14        result[i] = leftProduct;
15        leftProduct *= nums[i];
16    }
17  
18    // Second pass: Calculate right products and combine with left products
19    // For each position, multiply the stored left product by the product of all elements to its right
20    let rightProduct: number = 1;
21    for (let i: number = length - 1; i >= 0; i--) {
22        result[i] *= rightProduct;
23        rightProduct *= nums[i];
24    }
25  
26    return result;
27}
28

Time and Space Complexity

Time Complexity: O(n), where n is the length of the array nums. The algorithm performs two separate passes through the array - the first loop iterates through all n elements to calculate left products, and the second loop iterates through all n elements again to calculate right products and combine them with left products. Since both loops run sequentially, the total time complexity is O(n) + O(n) = O(2n) = O(n).

Space Complexity: O(1) (excluding the output array). The algorithm only uses two extra variables left and right to keep track of the running products, which requires constant space. The answer array ans of size n is required for the output and is not counted toward the space complexity as per the problem's convention. If we were to include the output array, the total space complexity would be O(n).

Common Pitfalls

1. Misunderstanding the Order of Operations in Each Pass

A frequent mistake is incorrectly ordering the assignment and multiplication operations within each loop. Developers often write:

# INCORRECT - First Pass
for i in range(n):
    left_product *= nums[i]  # Multiplying BEFORE storing
    result[i] = left_product  # This includes nums[i] in the product!

This error includes nums[i] in its own product, violating the problem requirement. The correct approach stores the product BEFORE multiplying:

# CORRECT - First Pass
for i in range(n):
    result[i] = left_product  # Store current product first
    left_product *= nums[i]   # Then update for next iteration

2. Attempting to Optimize by Combining Both Passes

Some developers try to calculate both left and right products in a single loop:

# INCORRECT - Trying to do both passes simultaneously
for i in range(n):
    result[i] = left_product
    result[n-1-i] *= right_product  # This overwrites left products!
    left_product *= nums[i]
    right_product *= nums[n-1-i]

This approach fails because the right-to-left calculations overwrite the left products before they can be properly combined. The two-pass approach is necessary to ensure each position receives both its left and right products.

3. Handling Edge Cases with Empty or Single-Element Arrays

While the provided solution handles these cases correctly, developers sometimes add unnecessary special case handling:

# UNNECESSARY complexity
if n == 0:
    return []
if n == 1:
    return [1]  # INCORRECT - should return [1] for input [x]

The two-pass algorithm naturally handles these cases:

  • For nums = []: Returns []
  • For nums = [5]: Returns [1] (product of zero elements is 1)

4. Using Division as a "Clever" Shortcut

When not carefully reading constraints, developers might attempt:

# VIOLATES CONSTRAINT - Uses division
total_product = 1
for num in nums:
    total_product *= num

for i in range(n):
    result[i] = total_product // nums[i]  # Division not allowed!

This also fails when any element is zero, causing division by zero errors.

Solution to Avoid These Pitfalls:

  1. Always store before updating: In each pass, assign the current accumulated product to the result array BEFORE multiplying by the current element.

  2. Maintain two separate passes: Don't try to combine them - the algorithm's elegance comes from the clean separation of left and right product calculations.

  3. Trust the algorithm: The two-pass approach naturally handles edge cases without special logic.

  4. Follow constraints strictly: Even if division seems simpler, the constraint exists for good reasons (handling zeros, demonstrating understanding of the two-pointer technique).

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

What data structure does Breadth-first search typically uses to store intermediate states?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More