Difficulty: Medium, Asked-in: Facebook, Amazon, Morgan Stanley
Key takeaway: This problem is an excellent opportunity to learn step-by-step optimization using prefix arrays and a single loop with variables.
Given an array X[] of n integers where n > 1, write a program to return an array product[] such that product[i] is equal to the product of all the elements of X except X[i].
Important note: Before moving on to the solutions, we recommend trying this problem on paper for at least 15 or 30 minutes. Enjoy problem-solving!
Input: X[] = [2, 1, 3, 4], Output: product[] = [12, 24, 8, 6]
Explanation:
Input: X[] = [5, 2, 8, 4, 5], Output: product[] = [320, 800, 200, 400, 320]
Input: X[] = [1, 0, 4, 3, 5], Output: product[] = [0, 60, 0, 0, 0]
Input: X[] = [1, 1, 1, 1, 1, 1, 1], Output: product[] = [1, 1, 1, 1, 1, 1, 1]
Input: X[] = [0, 4, 0, 3], Output: product[] = [0, 0, 0, 0]
The basic idea would be to traverse the array, find the product of each element except the current element, and store it in the output array. To implement this:
vector<int> productExceptSelf(int X[], int n)
{
vector<int> product(n);
for (int i = 0; i < n; i = i + 1)
{
int prodExclCurr = 1;
for (int j = 0; j < n; j = j + 1)
{
if (i == j)
continue;
prodExclCurr = prodExclCurr * X[j];
}
product[i] = prodExclCurr;
}
return product;
}
def productExceptSelf(X, n):
product = [0] * n
for i in range(n):
prodExclCurr = 1
for j in range(n):
if i == j:
continue
prodExclCurr = prodExclCurr*X[j]
product[i] = prodExclCurr
return product
We are running two nested loops and performing constant or O(1) operations at each iteration. So time complexity = O(n^2) * O(1) = O(n^2). We are using constant extra space, so space complexity = O(1).
We are running two nested loops and performing constant or O(1) operations at each iteration. So, the time complexity is O(n^2) * O(1) = O(n^2). Since we are using constant extra space, the space complexity is O(1).
Now the critical question is: can we solve this problem using a single loop in O(n) time complexity? Can we use extra space and store some intermediate values to get an O(n) solution? Let's think!
If we observe the output pattern, the product of all elements except X[i] is equal to the product of all elements from X[0] to X[i - 1] multiplied by the product of all elements from X[i + 1] to X[n - 1]. So, if we multiply the prefix product with the suffix product, we will get the product of elements excluding the current index.
To implement this idea, we need to create two extra arrays, prefixProduct[n] and suffixProduct[n], to store the prefix and suffix products for each index i. How do we store values in both arrays? Let's think!
Calculation of prefixProduct[n]: We can store the prefix array by calculating the running product using a single loop starting from the left. The idea is simple: if we know the value of prefixProduct[i - 1], we can easily calculate prefixProduct[i].
For i = 0, there is no element on the left of the array. So we initialize prefixProduct[0] with 1, and the loop will run from i = 1 to n - 1.
prefixProduct[0] = 1
for (int i = 1; i < n; i = i + 1)
prefixProduct[i] = X[i - 1] * prefixProduct[i - 1]
Calculation of suffixProduct[n]: Similarly, we can store the suffix product array by calculating the running product using a single loop starting from the right end. The idea is simple: if we know the value of suffixProduct[i+1], we can easily calculate suffixProduct[i].
For i = n-1, there is no element to the right of the array. So we initialize suffixProduct[n-1] with 1 and the loop will run from i = n-2 to 0.
suffixProduct[n - 1] = 1
for (int i = n - 2; i >= 0; i = i - 1)
suffixProduct[i] = X[i + 1] * suffixProduct[i + 1]
Finally, we run another single loop to store the product excluding each element in the output array. product[i] = prefixProduct[i] * suffixProduct[i]
for (int i = 0; i < n; i = i + 1)
product[i] = prefixProduct[i] * suffixProduct[i]
vector<int> productExceptSelf(int X[], int n)
{
vector<int> prefixProduct(n);
vector<int> suffixProduct(n);
vector<int> product(n);
prefixProduct[0] = 1;
for (int i = 1; i < n; i = i + 1)
prefixProduct[i] = X[i - 1] * prefixProduct[i - 1];
suffixProduct[n - 1] = 1;
for (int i = n - 2; i >= 0; i = i - 1)
suffixProduct[i] = X[i + 1] * suffixProduct[i + 1];
for (int i = 0; i < n; i = i + 1)
product[i] = prefixProduct[i] * suffixProduct[i];
return product;
}
def productExceptSelf(X, n):
prefixProduct = [0] * n
suffixProduct = [0] * n
product = [0] * n
prefixProduct[0] = 1
for i in range(1, n):
prefixProduct[i] = X[i-1] * prefixProduct[i-1]
suffixProduct[n-1] = 1
for i in range(n-2, -1, -1):
suffixProduct[i] = X[i+1] * suffixProduct[i+1]
for i in range(n):
product[i] = prefixProduct[i] * suffixProduct[i]
return product
We are using three single loops, where each loop runs n times. Time complexity = Time complexity to store prefixProduct[] + Time complexity to store suffixProduct[] + Time complexity to store values in product[] = O(n) + O(n) + O(n) = O(n).
We are using two extra arrays of size n. So space complexity = O(n) + O(n) = O(n).
The critical question is: Can we optimize the above method to work in O(1) space complexity? Do we need to store both prefix and suffix product arrays? Think!
To find the product of all elements except X[i], we need two pieces of information: the prefix product from i=0 to i-1 and the suffix product from i=n-1 to i+1. So we can make two modifications to the previous solution:
Note: By traversing from the right end, we can do two things together: calculating suffixProduct and storing the final output in the product[].
for (int i = 1; i < n; i = i + 1)
product[i] = X[i - 1] * product[i - 1]
int suffixProduct = 1
for (int i = n - 1; i >= 0; i = i - 1)
{
product[i] = product[i] * suffixProduct
suffixProduct = suffixProduct * X[i]
}
vector<int> productExceptSelf(int X[], int n)
{
vector<int> product(n);
product[0] = 1;
for (int i = 1; i < n; i = i + 1)
product[i] = X[i - 1] * product[i - 1];
int suffixProduct = 1;
for (int i = n - 1; i >= 0; i = i - 1)
{
product[i] *= suffixProduct;
suffixProduct *= X[i];
}
return product;
}
def productExceptSelf(X, n):
product = [0] * n
product[0] = 1
for i in range(1, n):
product[i] = X[i-1] * product[i-1]
suffixProduct = 1
for i in range(n-1, -1, -1):
product[i] = product[i] * suffixProduct
suffixProduct = suffixProduct * X[i]
return product
If you have any queries/doubts/feedback, please write us at contact@enjoyalgorithms.com. Enjoy learning, Enjoy algorithms!