Product of Array Except Self

In this blog, we will discuss the "Product of Array Except Self" problem, which is a popular problem in coding interviews. We will go through the problem statement, explain the approach, and provide both brute force and optimal solutions. Additionally, we will perform a dry run of the code for better understanding and analyze the time and space complexity of each solution.

Problem Statement with Example

Given an integer array nums, the task is to return an array answer such that answer[i] is equal to the product of all the elements of nums except nums[i]. The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. You must write an algorithm that runs in O(n) time and without using the division operation.

Let's consider an example to understand the problem:

Example 1:

Input: nums = [1, 2, 3, 4]

Output: [24, 12, 8, 6]

Explanation: The product of all elements except the element at index 0 is 2*3*4 = 24. Similarly, for index 1, it is 1*3*4 = 12, for index 2, it is 1*2*4 = 8, and for index 3, it is 1*2*3 = 6.

Example 2:

Input: nums = [-1, 1, 0, -3, 3]

Output: [0, 0, 9, 0, 0]

Explanation: The product of all elements except the element at index 0 is 1*0*(-3)*3 = 0. Similarly, for index 1, it is -1*0*(-3)*3 = 0, for index 2, it is -1*1*(-3)*3 = 9, for index 3, it is -1*1*0*3 = 0, and for index 4, it is -1*1*0*(-3) = 0.

Practice: Leetcode Link Leetcode

Brute Force Solution

We can solve this problem using a brute force approach by iterating over the array and calculating the product of all elements except the current one for each element. However, this approach will have a time complexity of O(n^2), which is not efficient for large arrays.

Algorithm/Pseudocode


// Brute Force Approach
1. Initialize an array 'answer' of the same length as 'nums' and fill it with 1.
2. For each element in 'nums' at index i:
    a. Initialize a variable 'product' to 1.
    b. For each element in 'nums' at index j:
        i. If i is not equal to j, multiply 'product' by nums[j].
    c. Assign 'product' to answer[i].
3. Return the 'answer' array.

Java Code for Brute Force Solution

package codeKatha;

public class ProductOfArrayExceptSelf {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] answer = new int[n];

        for (int i = 0; i < n; i++) {
            int product = 1;
            for (int j = 0; j < n; j++) {
                if (i != j) {
                    product *= nums[j];
                }
            }
            answer[i] = product;
        }

        return answer;
    }
}

Dry Run

Let's dry run the brute force solution with an example:

Input: nums = [1, 2, 3, 4]

Step 1: Initialize answer = [1, 1, 1, 1]

Step 2: Iterating over each element in nums:
- For i = 0: product = 1
    - For j = 0: skip (i == j)
    - For j = 1: product = 1 * 2 = 2
    - For j = 2: product = 2 * 3 = 6
    - For j = 3: product = 6 * 4 = 24
    - answer[0] = 24

- For i = 1: product = 1
    - For j = 0: product = 1 * 1 = 1
    - For j = 1: skip (i == j)
    - For j = 2: product = 1 * 3 = 3
    - For j = 3: product = 3 * 4 = 12
    - answer[1] = 12

- For i = 2: product = 1
    - For j = 0: product = 1 * 1 = 1
    - For j = 1: product = 1 * 2 = 2
    - For j = 2: skip (i == j)
    - For j = 3: product = 2 * 4 = 8
    - answer[2] = 8

- For i = 3: product = 1
    - For j = 0: product = 1 * 1 = 1
    - For j = 1: product = 1 * 2 = 2
    - For j = 2: product = 2 * 3 = 6
    - For j = 3: skip (i == j)
    - answer[3] = 6

Step 3: Return answer = [24, 12, 8, 6]

Time and Space Complexity

Time Complexity: O(n2) - For each element, we are iterating through the entire array to calculate the product.

Space Complexity: O(n) - We are using an extra array to store the result.

Optimized Solution

To optimize this solution, we can use two additional arrays to store the product of all elements to the left and right of each element. This will allow us to calculate the final product for each element in O(n) time.

Algorithm/Pseudocode


// Optimized Approach
1. Initialize two arrays, 'leftProducts' and 'rightProducts', of the same length as 'nums' and fill them with 1.
2. Initialize an array 'answer' of the same length as 'nums' and fill it with 1.
3. Fill 'leftProducts' such that leftProducts[i] contains the product of all elements to the left of 'nums[i]'.
4. Fill 'rightProducts' such that rightProducts[i] contains the product of all elements to the right of 'nums[i]'.
5. For each element in 'nums' at index i:
    a. answer[i] = leftProducts[i] * rightProducts[i].
6. Return the 'answer' array.

Java Code for Optimized Solution

package codeKatha;

public class ProductOfArrayExceptSelf {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] leftProducts = new int[n];
        int[] rightProducts = new int[n];
        int[] answer = new int[n];

        // Fill leftProducts
        leftProducts[0] = 1;
        for (int i = 1; i < n; i++) {
            leftProducts[i] = nums[i - 1] * leftProducts[i - 1];
        }

        // Fill rightProducts
        rightProducts[n - 1] = 1;
        for (int i = n - 2; i >= 0; i--) {
            rightProducts[i] = nums[i + 1] * rightProducts[i + 1];
        }

        // Fill answer
        for (int i = 0; i < n; i++) {
            answer[i] = leftProducts[i] * rightProducts[i];
        }

        return answer;
    }
}

Dry Run

Let's dry run the optimized solution with an example:

Input: nums = [1, 2, 3, 4]

Step 1: Initialize leftProducts = [1, 1, 1, 1]
Step 2: Initialize rightProducts = [1, 1, 1, 1]
Step 3: Initialize answer = [1, 1, 1, 1]

Step 4: Fill leftProducts:
- leftProducts[1] = 1 * 1 = 1
- leftProducts[2] = 2 * 1 = 2
- leftProducts[3] = 3 * 2 = 6

leftProducts = [1, 1, 2, 6]

Step 5: Fill rightProducts:
- rightProducts[2] = 4 * 1 = 4
- rightProducts[1] = 3 * 4 = 12
- rightProducts[0] = 2 * 12 = 24

rightProducts = [24, 12, 4, 1]

Step 6: Fill answer:
- answer[0] = 1 * 24 = 24
- answer[1] = 1 * 12 = 12
- answer[2] = 2 * 4 = 8
- answer[3] = 6 * 1 = 6

Step 7: Return answer = [24, 12, 8, 6]

Time and Space Complexity

Time Complexity: O(n)- We are iterating through the array three times, which is linear.

Space Complexity: O(n) - We are using two extra arrays to store the left and right products.

More Optimized Solution

We can further optimize the solution by using the output array itself to store the left products, and we can calculate the right products on the fly.

Algorithm/Pseudocode


// More Optimized Approach
1. Initialize an array 'answer' of the same length as 'nums' and fill it with 1.
2. Fill 'answer' such that answer[i] contains the product of all elements to the left of 'nums[i]'.
3. Initialize a variable 'rightProduct' to 1.
4. Traverse the array from right to left and update 'answer' such that it contains the product of all elements except 'nums[i]'.
5. Return the 'answer' array.

Java Code for More Optimized Solution

package codeKatha;

public class ProductOfArrayExceptSelf {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] answer = new int[n];

        // Fill answer with left products
        answer[0] = 1;
        for (int i = 1; i < n; i++) {
            answer[i] = nums[i - 1] * answer[i - 1];
        }

        // Calculate right products on the fly and update answer
        int rightProduct = 1;
        for (int i = n - 1; i >= 0; i--) {
            answer[i] *= rightProduct;
            rightProduct *= nums[i];
        }

        return answer;
    }
}

Dry Run

Let's dry run the more optimized solution with an example:

Input: nums = [1, 2, 3, 4]

Step 1: Initialize answer = [1, 1, 1, 1]

Step 2: Fill answer with left products:
- answer[1] = 1 * 1 = 1
- answer[2] = 2 * 1 = 2
- answer[3] = 3 * 2 = 6

answer = [1, 1, 2, 6]

Step 3: Initialize rightProduct = 1

Step 4: Traverse from right to left and update answer:
- i = 3: answer[3] *= 1 => answer[3] = 6
           rightProduct *= 4 => rightProduct = 4
- i = 2: answer[2] *= 4 => answer[2] = 8
           rightProduct *= 3 => rightProduct = 12
- i = 1: answer[1] *= 12 => answer[1] = 12
           rightProduct *= 2 => rightProduct = 24
- i = 0: answer[0] *= 24 => answer[0] = 24
           rightProduct *= 1 => rightProduct = 24

Step 5: Return answer = [24, 12, 8, 6]

Time and Space Complexity

Time Complexity: O(n)- We are iterating through the array three times, which is linear.

Space Complexity: O(1) - We are using the output array itself to store the left products and calculating the right products on the fly.

Edge Cases

1. If the input array contains only one element, the output should be an empty array or an array with a single element 1 because there are no other elements to multiply.

2. If the input array contains zero, the product for the indices other than the zero index will be zero unless there is more than one zero.

By understanding these solutions, even a novice can grasp the concepts and apply the optimal approach for solving the "Product of Array Except Self" problem efficiently.

Comments

Popular Posts on Code Katha

Java Interview Questions for 10 Years Experience

Sql Interview Questions for 10 Years Experience

Spring Boot Interview Questions for 10 Years Experience

Java interview questions - Must to know concepts

Visual Studio Code setup for Java and Spring with GitHub Copilot

Data Structures & Algorithms Tutorial with Coding Interview Questions

Spring AI with Ollama

Spring Data JPA

Topological Sort in Graph

Bit Manipulation and Bit Masking Concepts