Maximum Product Subarray
Problem
nums containing positives, negatives and
zeros, find the non-empty contiguous block whose elements multiply to the
largest product, and return that product.[2,3,-2,4]Output6 ([2,3])[-2,0,-1]Output0[-2,3,-4]Output241 ≤ n ≤ 2·104 · -10 ≤ nums[i] ≤ 10 · every subarray product fits in a 32-bit int
Try it yourself
class Solution {
public int maxProduct(int[] nums) {
}
}
Instinct Best sum or product of a contiguous subarray in one pass? At each index choose - extend the run ending here or restart - and track the global best.
Memory hack Carry the bag only while it helps - a negative past is dead weight, drop it and restart here.
Open the Kadane's Running Best guide →Approaches
Brute ForceAll Subarrays, Running Product
O(n2)Space O(1)Fix each start i, extend j and multiply a running
product by nums[j], so each subarray is evaluated in O(1). Keep the largest
product seen.
public int maxProduct(int[] nums) { int maxProduct = Integer.MIN_VALUE; for (int i = 0; i < nums.length; i++) { int product = 1; for (int j = i; j < nums.length; j++) { product *= nums[j]; // product of nums[i..j] maxProduct = Math.max(maxProduct, product); } } return maxProduct; }
Straightforward, but quadratic — too slow for n = 2·104 in many judges.
BetterDP Tables (max & min ending here)
O(n)Space O(n)hi[i] / lo[i] = largest / smallest product of a subarray
ending at i. Each is one of three candidates: nums[i] alone,
hi[i-1]·nums[i] or lo[i-1]·nums[i]. Answer =
max of hi[].
public int maxProduct(int[] nums) { int n = nums.length; int[] hi = new int[n], lo = new int[n]; hi[0] = lo[0] = nums[0]; int best = nums[0]; for (int i = 1; i < n; i++) { int x = nums[i], a = hi[i - 1] * x, b = lo[i - 1] * x; hi[i] = Math.max(x, Math.max(a, b)); lo[i] = Math.min(x, Math.min(a, b)); best = Math.max(best, hi[i]); } return best; }
Linear and easy to verify, but keeps two arrays where two variables suffice.
OptimalRolling Max/Min (Kadane Variant)
O(n)Space O(1)Only the previous hi/lo are needed, so roll them in two
variables. When x < 0, swap them first: multiplying by a negative
makes the old min the new max candidate. Then max = max(x, max·x),
min = min(x, min·x).
public int maxProduct(int[] nums) { int maxEnd = nums[0], minEnd = nums[0], best = nums[0]; for (int i = 1; i < nums.length; i++) { int x = nums[i]; if (x < 0) { int tmp = maxEnd; maxEnd = minEnd; minEnd = tmp; } // sign flip maxEnd = Math.max(x, maxEnd * x); minEnd = Math.min(x, minEnd * x); best = Math.max(best, maxEnd); } return best; }
Dry run[2,3,-2,4,-1]
- i=02i3-24-1max=2, min=2, best=2
- i=123i-24-1max=6, min=3, best=6
- i=223-2i4-1swap; max=-2, min=-12
- i=323-24i-1max=4, min=-48
- i=423-24-1iswap; max=48 → best=48 ✔
Single pass, constant space — the expected answer.
At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | All Subarrays, Running Product | O(n2) | O(1) |
| Better | DP Tables (max & min ending here) | O(n) | O(n) |
| Optimal | Rolling Max/Min (Kadane Variant) | O(n) | O(1) |
- Tracking only the max (plain Kadane) —
[-2,3,-4]gives 3 instead of 24. - Updating
maxthen using the new max to computemin— save it first or swap. - Initialising
best = 0— wrong for[-3].
- Start from Maximum Subarray, then show the counter-example that breaks plain Kadane.
- Explain zeros: they make both
maxandmincollapse tox, i.e. a restart. - Mention overflow: use
long/doubleif products are not guaranteed to fit.