Problem Statement in English
You’re given an array nums. Your task is to return an array where array[i] is the product of values at all indices in the array except $i$.
Further, you must solution must run in linear time, $O(n)$, and you must not use division!
Approach
This problem would have been really easy had it not been for the restriction on division operations. But no matter, we have a trick up our sleeve: Prefix Sums. Let’s deviate for just a bit and get the hang of this technique.
Imagine you had an array arr, say you had to return an array where each index contained the sum of all values at all indices before it, and you had to do it in linear time, without subtraction. Sounds a little like the original problem? That’s because it is. You can use the exact same approach. Here’s what the answer would look like:
arr = [1,2,3,4], ans = [0,1,3,6]
But how do we do it? Instead of me explaining, try going through this code snippet and understanding. It’s way shorter than any explanation I can provide.
ans = []
for i in range(len(arr)):
if i == 0:
ans.append(0)
else:
ans.append(ans[-1]+arr[i-1])
return ans
We utilise a similar approach with the original problem. But since we need the product of values at indices before and after this one, we’ll need to make a forward and backward pass.
Step 1: Forward Pass
- Step 1.1: Store the product of all terms before index $i$, at
leftPass[i]
Step 2: Backward Pass
- Step 2.1: Store the product of all terms after index $i$, at
leftPass[i]
Step 3: Combined Pass
- Step 3.1: Calculate value at index $i$ as
leftPass[i]*rightPass[i]
Solution in Python
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
# we store products calculated
# in the left pass here
forwardPass = []
# we store products calculated
# in the right pass here
backwardPass = []
# we store our final answer here
ans = []
# forward pass
for i in range(len(nums)):
if i == 0:
forwardPass.append(1)
else:
forwardPass.append(forwardPass[-1]*nums[i-1])
pass
# backward pass
for i in range(len(nums)-1, -1, -1):
if i == len(nums)-1:
backwardPass.insert(0, 1)
else:
backwardPass.insert(0, backwardPass[0]*nums[i+1])
pass
# combined pass
for i in range(len(nums)):
ans.append(forwardPass[i]*backwardPass[i])
return ans
Complexity
Time: $O(n)$
Since we iterate over $n$ elements atmost. The fact that we iterate over them thrice is inconsequential, since this is Big O complexity.Space: $O(n)$
Since we store at most $n$ elements. The fact that we store $3\text{n}$ elements is inconsequential, since this is Big O complexity.
Mistakes I Made
This approach didn’t occur to me. I needed a nudge in the right direction - the question tags.
And we are done.