Product of every element except itself
Given an array nums, return an array out where
out[i] is the product of every element of nums except
nums[i]. Do not use division, and aim for O(n) time.
Every product fits in a signed 32-bit integer.
Examples
| Input | Output | Why |
|---|---|---|
nums = [1, 2, 3, 4] | [24, 12, 8, 6] | out[1] = 1 × 3 × 4 = 12. |
nums = [5, 0, 2] | [0, 10, 0] | Only the zero's own slot escapes the zero. |
nums = [0, 0, 5] | [0, 0, 0] | Two zeros: every product contains at least one. |
Constraints
- 2 ≤ n ≤ 10⁵
- −30 ≤ nums[i] ≤ 30
- Every product fits in 32 bits
- No division
Hints
Hint 1
Total product divided by nums[i] is the obvious move. Try it on [5, 0, 2] and see what happens, and why the question bans it anyway.
Hint 2
The product of everything except position i splits cleanly into two pieces: everything to its left, and everything to its right.
Hint 3
One pass left to right fills in the left products. A second pass right to left multiplies in the right products, carried in a single running variable.
Dry run on [1,2,3,4]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 11nums10213243out10111213left1
Two passes. First, left to right: slot i gets the product of everything strictly to its left.
-
Step 2 / 11nums10i213243out10111213left1
out[0] = 1, the product of everything left of index 0. Then multiply 1 into the running product.
-
Step 3 / 11nums1021i3243out10111213left1
out[1] = 1, the product of everything left of index 1. Then multiply 2 into the running product.
-
Step 4 / 11nums102132i43out10112213left2
out[2] = 2, the product of everything left of index 2. Then multiply 3 into the running product.
-
Step 5 / 11nums10213243iout10112263left6
out[3] = 6, the product of everything left of index 3. Then multiply 4 into the running product.
-
Step 6 / 11nums10213243out10112263right1
Now right to left, carrying the product of everything strictly to the right in one variable.
-
Step 7 / 11nums10213243iout10112263right1
out[3] × 1 = 6: left product times right product. Then multiply 4 into the running right product.
-
Step 8 / 11nums102132i43out10118263right4
out[2] × 4 = 8: left product times right product. Then multiply 3 into the running right product.
-
Step 9 / 11nums1021i3243out101218263right12
out[1] × 12 = 12: left product times right product. Then multiply 2 into the running right product.
-
Step 10 / 11nums10i213243out2401218263right24
out[0] × 24 = 24: left product times right product. Then multiply 1 into the running right product.
-
Step 11 / 11out2401218263
No division anywhere, so a zero needed no special case. Answer [24, 12, 8, 6].
How to think about it
The shortcut is to multiply everything once and divide by each element. It fails on a zero — you cannot divide by it, and with two zeros the total is zero while the bookkeeping gets worse — and the question bans division precisely so that you find the structure underneath.
That structure is simple. The product of everything except position i is
(product of nums[0 .. i−1]) × (product of nums[i+1 .. n−1])
— a prefix times a suffix. Build the prefixes in one pass left to right, writing each into the
output before multiplying in the current element, so that slot i holds the product of
everything strictly to its left. Then sweep right to left with a single running product of
everything strictly to the right, and multiply it in.
Zeros need no special handling: a zero simply appears in the prefix or the suffix of every slot but its own. This is the same move as prefix sums in the subarray problem — store the running total, and let a range become the combination of two of them — with multiplication in place of addition.
Complexity. Time O(n) · Space O(1) beyond the output
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def product_except_self(nums):
n = len(nums)
out = [1] * n
left = 1
for i in range(n): # out[i] = product of everything left of i
out[i] = left
left *= nums[i]
right = 1
for i in range(n - 1, -1, -1): # multiply in everything right of i
out[i] *= right
right *= nums[i]
return out#include <vector>
using namespace std;
vector<int> solve(vector<int> nums) {
int n = nums.size();
vector<int> out(n, 1);
int left = 1;
for (int i = 0; i < n; i++) { // out[i] = product of everything left of i
out[i] = left;
left *= nums[i];
}
int right = 1;
for (int i = n - 1; i >= 0; i--) { // multiply in everything right of i
out[i] *= right;
right *= nums[i];
}
return out;
}class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] out = new int[n];
int left = 1;
for (int i = 0; i < n; i++) { // out[i] = product of everything left of i
out[i] = left;
left *= nums[i];
}
int right = 1;
for (int i = n - 1; i >= 0; i--) { // multiply in everything right of i
out[i] *= right;
right *= nums[i];
}
return out;
}
}function productExceptSelf(nums) {
const n = nums.length, out = new Array(n).fill(1);
let left = 1;
for (let i = 0; i < n; i++) { // out[i] = product of everything left of i
out[i] = left;
left *= nums[i];
}
let right = 1;
for (let i = n - 1; i >= 0; i--) { // multiply in everything right of i
out[i] *= right;
right *= nums[i];
}
return out;
}fn solve(nums: &[i32]) -> Vec<i32> {
let n = nums.len();
let mut out = vec![1; n];
let mut left = 1;
for i in 0..n { // out[i] = product of everything left of i
out[i] = left;
left *= nums[i];
}
let mut right = 1;
for i in (0..n).rev() { // multiply in everything right of i
out[i] *= right;
right *= nums[i];
}
out
}-- scanl gives the products to the left of each slot, scanr those to the right.
solve :: [Int] -> [Int]
solve xs = zipWith (*) (init (scanl (*) 1 xs)) (tail (scanr (*) 1 xs))
Where people lose marks
- Dividing the total product by nums[i]. Banned, and wrong in the presence of a zero.
- Including nums[i] in its own prefix — write the running product into out[i] before multiplying the current element in.
- Allocating separate prefix and suffix arrays. Correct, but O(n) extra space the running variable makes unnecessary; interviewers often ask for exactly this improvement.
- Overflow when the constraints are looser than here. Say which type you would widen to, and when.
Variants to try
- Division is allowed. (Count zeros: none, divide; one, only that slot is non-zero; two or more, all zero.)
- Same question, but sums instead of products. (Total minus nums[i] — addition has an inverse that never fails.)
- Answer queries "product of nums[l..r]" many times. (Prefix products, with care around zeros — store the zero count as a prefix too.)
Next problems
Pair with a given sum in an unsorted array
Every pair is n² work. Remembering what you have already passed makes it n, and the order of two lines decides whether it is right.
Open the problem → Medium15 minLongest run of consecutive integers
Sorting gives the answer in O(n log n). A set gives it in O(n), and the reason it is not O(n²) is the interesting part.
Open the problem → Medium15 minThe k most frequent values
A hash map does the counting in one pass. The interesting part is ordering what you counted, and saying precisely how ties break.
Open the problem →Write a solution and run it against the real test table.
Keep going
Pair with a given sum in an unsorted array
Every pair is n² work. Remembering what you have already passed makes it n, and the order of two lines decides whether it is right.
Open the problem → Medium15 minLongest run of consecutive integers
Sorting gives the answer in O(n log n). A set gives it in O(n), and the reason it is not O(n²) is the interesting part.
Open the problem → Medium15 minThe k most frequent values
A hash map does the counting in one pass. The interesting part is ordering what you counted, and saying precisely how ties break.
Open the problem → Arrays and hashingThe pattern behind it
Trade memory for time: a hash map answers "have I seen this?" in O(1), and most array problems reduce to asking it the right question.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.