A clear explanation of finding the largest product of three numbers using sorting or constant-space tracking.
Problem Restatement
We are given an integer array nums.
We need to choose three numbers from the array so that their product is as large as possible.
Return that maximum product.
The three numbers do not need to be next to each other. We only care about choosing any three different elements.
Input and Output
| Item | Meaning |
|---|---|
| Input | An integer array nums |
| Output | The maximum product of any three numbers |
| Constraint | nums.length >= 3 |
Example function shape:
def maximumProduct(nums: list[int]) -> int:
...Examples
Example 1:
nums = [1, 2, 3]The only possible product is:
1 * 2 * 3 = 6So the answer is:
6Example 2:
nums = [1, 2, 3, 4]The best three numbers are 2, 3, and 4.
2 * 3 * 4 = 24So the answer is:
24Example 3:
nums = [-1, -2, -3]There are exactly three numbers, so we must use all of them.
-1 * -2 * -3 = -6So the answer is:
-6First Thought: Try Every Triple
The direct solution is to check every possible group of three numbers.
For every i, j, and k, where all indices are different, compute:
nums[i] * nums[j] * nums[k]Then keep the largest product.
Code:
class Solution:
def maximumProduct(self, nums: list[int]) -> int:
n = len(nums)
best = float("-inf")
for i in range(n):
for j in range(i + 1, n):
for k in range(j + 1, n):
best = max(best, nums[i] * nums[j] * nums[k])
return bestThis is correct, but it checks too many triples.
For n numbers, this takes O(n^3) time.
Key Insight
The maximum product must come from one of two cases.
The first case is simple: use the three largest numbers.
largest * second_largest * third_largestThis works when the best product comes from large positive values.
But negative numbers change the problem.
Two negative numbers multiply into a positive number. So the second possible answer is:
largest * smallest * second_smallestFor example:
nums = [-10, -10, 5, 2]The three largest numbers are -10, 2, and 5 if sorted carefully by value near the end we get:
-10 * 2 * 5 = -100But the two smallest numbers are -10 and -10.
-10 * -10 * 5 = 500So the answer is 500.
That means we only need five important values:
| Value | Why |
|---|---|
| Largest number | Used in both possible best products |
| Second largest number | Needed for three-largest case |
| Third largest number | Needed for three-largest case |
| Smallest number | May be a large negative |
| Second smallest number | May pair with the smallest negative |
Algorithm: Sorting
Sort the array in ascending order.
After sorting:
| Expression | Meaning |
|---|---|
nums[-1] | largest number |
nums[-2] | second largest number |
nums[-3] | third largest number |
nums[0] | smallest number |
nums[1] | second smallest number |
Then compute two candidate products:
candidate1 = nums[-1] * nums[-2] * nums[-3]
candidate2 = nums[-1] * nums[0] * nums[1]Return the larger one.
Implementation
class Solution:
def maximumProduct(self, nums: list[int]) -> int:
nums.sort()
return max(
nums[-1] * nums[-2] * nums[-3],
nums[-1] * nums[0] * nums[1]
)Code Explanation
First, we sort the array:
nums.sort()This puts the smallest values at the front and the largest values at the back.
The product of the three largest values is:
nums[-1] * nums[-2] * nums[-3]The product of the largest value and the two smallest values is:
nums[-1] * nums[0] * nums[1]This handles arrays with two large negative numbers.
Finally, we return the better of the two products:
return max(...)Correctness
After sorting, nums[-1], nums[-2], and nums[-3] are the three largest values in the array.
If the maximum product uses three large positive numbers, then it is exactly:
nums[-1] * nums[-2] * nums[-3]If the maximum product uses negative numbers, it must use two negative numbers, because one negative number would make the product negative when multiplied by positive numbers. To make this product as large as possible, we want the two smallest numbers, since they are the most negative and give the largest positive product when multiplied together.
So that candidate is:
nums[-1] * nums[0] * nums[1]Any maximum product must be one of these two forms. The algorithm computes both and returns the larger value, so it returns the maximum possible product.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n log n) | Sorting dominates the runtime |
| Space | O(1) or O(n) | Depends on the sorting implementation |
The main idea is simple and reliable. Sorting gives direct access to the only values that can matter.
Alternative Solution: One Pass
We can solve this without sorting.
Track the three largest numbers and the two smallest numbers while scanning the array once.
class Solution:
def maximumProduct(self, nums: list[int]) -> int:
min1 = float("inf")
min2 = float("inf")
max1 = float("-inf")
max2 = float("-inf")
max3 = float("-inf")
for num in nums:
if num <= min1:
min2 = min1
min1 = num
elif num <= min2:
min2 = num
if num >= max1:
max3 = max2
max2 = max1
max1 = num
elif num >= max2:
max3 = max2
max2 = num
elif num >= max3:
max3 = num
return max(
max1 * max2 * max3,
max1 * min1 * min2
)This version has better asymptotic time complexity.
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | One scan through the array |
| Space | O(1) | Only five variables are stored |
Testing
def run_tests():
s = Solution()
assert s.maximumProduct([1, 2, 3]) == 6
assert s.maximumProduct([1, 2, 3, 4]) == 24
assert s.maximumProduct([-1, -2, -3]) == -6
assert s.maximumProduct([-10, -10, 5, 2]) == 500
assert s.maximumProduct([-100, -2, -3, 1]) == 300
assert s.maximumProduct([0, 0, 0]) == 0
assert s.maximumProduct([-5, -4, -3, -2, -1]) == -6
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
[1, 2, 3] | Minimum length |
[1, 2, 3, 4] | Normal positive case |
[-1, -2, -3] | All negative with exactly three values |
[-10, -10, 5, 2] | Two negatives create the maximum |
[-100, -2, -3, 1] | Smallest two values matter |
[0, 0, 0] | Product can be zero |
[-5, -4, -3, -2, -1] | All negative values |