414. Third Maximum Number
Problem Description
You are given an integer array nums
. Your task is to find and return the third distinct maximum number in this array.
The key requirements are:
- The number must be the third largest distinct value (duplicates don't count as separate maximums)
- If there aren't three distinct numbers in the array, return the maximum number instead
For example:
- If
nums = [3, 2, 1]
, the three distinct numbers are 3, 2, and 1, so the third maximum is 1 - If
nums = [1, 2]
, there are only two distinct numbers, so we return the maximum which is 2 - If
nums = [2, 2, 3, 1]
, the distinct numbers are 3, 2, and 1 (the duplicate 2 is ignored), so the third maximum is 1
The solution tracks the three largest distinct values using variables m1
, m2
, and m3
representing the first, second, and third maximum respectively. It processes each number in the array once, updating these maximums as needed while skipping duplicates. The cascading update pattern (shifting values down when a new maximum is found) ensures the three variables always hold the three largest distinct values seen so far.
Intuition
The natural approach to finding the third maximum would be to sort the array, remove duplicates, and return the third element. However, this would require O(n log n)
time for sorting. Can we do better?
Since we only need to track the top 3 distinct values, we don't need to maintain the entire sorted order. Think of it like having three podium positions - gold, silver, and bronze. As we scan through the array, we only need to update these three positions when we encounter a new contender.
The key insight is that when we find a number larger than our current maximum, it triggers a cascade effect:
- The current 1st place moves to 2nd place
- The current 2nd place moves to 3rd place
- The current 3rd place gets knocked off the podium
- The new number takes 1st place
Similarly, if a number is between the 1st and 2nd maximum, it only affects positions 2 and 3. This cascading update pattern naturally maintains the correct order.
We initialize all three variables to negative infinity (-inf
) as a sentinel value. This serves two purposes:
- Any actual number in the array will be greater than
-inf
, ensuring proper updates - At the end, if
m3
is still-inf
, we know we never found three distinct numbers
The duplicate check if num in [m1, m2, m3]
ensures we only consider distinct values, as the problem requires distinct maximums, not just the three largest occurrences.
This approach gives us O(n)
time complexity with just a single pass through the array, and O(1)
space complexity since we only use three variables regardless of input size.
Solution Approach
We implement a single-pass algorithm using three variables to track the top three distinct maximums.
Initialization:
Set three variables m1
, m2
, and m3
to negative infinity (-inf
). These represent the first, second, and third largest numbers respectively. Using -inf
ensures any actual number in the array will be greater than our initial values.
Main Loop:
For each number num
in the array:
-
Skip duplicates: Check if
num
equals any ofm1
,m2
, orm3
. If it does, skip to the next iteration usingcontinue
. This ensures we only track distinct values. -
Update maximums based on comparison:
-
If
num > m1
: This number is our new maximum. Perform cascading updates:m3, m2, m1 = m2, m1, num
The old first maximum becomes the second, old second becomes third, and
num
becomes the new first. -
Else if
num > m2
: This number fits between first and second. Update:m3, m2 = m2, num
The old second maximum becomes third, and
num
becomes the new second. -
Else if
num > m3
: This number only beats the third maximum:m3 = num
Simply update
m3
to this number.
-
Return Logic: After processing all numbers:
- If
m3 != -inf
: We found at least three distinct numbers, returnm3
- Otherwise: Less than three distinct numbers exist, return
m1
(the maximum)
The algorithm maintains the invariant that m1 >= m2 >= m3
for all non-infinity values, and each variable holds a distinct value. The time complexity is O(n)
with a single pass, and space complexity is O(1)
using only three variables.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [2, 2, 3, 1]
:
Initial State:
m1 = -inf
,m2 = -inf
,m3 = -inf
Processing num = 2:
- Check duplicates: 2 not in [-inf, -inf, -inf] โ
- Compare: 2 > -inf (m1)
- Cascade update:
m3 = m2 = -inf
,m2 = m1 = -inf
,m1 = 2
- New state:
m1 = 2
,m2 = -inf
,m3 = -inf
Processing num = 2 (duplicate):
- Check duplicates: 2 is in [2, -inf, -inf]
- Skip this iteration (continue)
- State unchanged:
m1 = 2
,m2 = -inf
,m3 = -inf
Processing num = 3:
- Check duplicates: 3 not in [2, -inf, -inf] โ
- Compare: 3 > 2 (m1)
- Cascade update:
m3 = m2 = -inf
,m2 = m1 = 2
,m1 = 3
- New state:
m1 = 3
,m2 = 2
,m3 = -inf
Processing num = 1:
- Check duplicates: 1 not in [3, 2, -inf] โ
- Compare: 1 < 3 (m1), so check next condition
- Compare: 1 < 2 (m2), so check next condition
- Compare: 1 > -inf (m3)
- Update:
m3 = 1
- Final state:
m1 = 3
,m2 = 2
,m3 = 1
Return Logic:
- Since
m3 = 1 โ -inf
, we found three distinct numbers - Return
m3 = 1
The algorithm correctly identifies the three distinct values (3, 2, 1) and returns the third maximum, which is 1.
Solution Implementation
1class Solution:
2 def thirdMax(self, nums: List[int]) -> int:
3 # Initialize three variables to track the top 3 maximum values
4 # Use negative infinity as initial values to handle negative numbers
5 first_max = second_max = third_max = float('-inf')
6
7 # Iterate through each number in the array
8 for num in nums:
9 # Skip duplicates - we only want distinct maximum values
10 if num in [first_max, second_max, third_max]:
11 continue
12
13 # Update the three maximum values based on current number
14 if num > first_max:
15 # New largest number found - shift all values down
16 third_max, second_max, first_max = second_max, first_max, num
17 elif num > second_max:
18 # New second largest number found - shift second and third down
19 third_max, second_max = second_max, num
20 elif num > third_max:
21 # New third largest number found - only update third
22 third_max = num
23
24 # Return third maximum if it exists, otherwise return the maximum
25 # If third_max is still -inf, it means we have less than 3 distinct numbers
26 return third_max if third_max != float('-inf') else first_max
27
1class Solution {
2 public int thirdMax(int[] nums) {
3 // Initialize three variables to track the top 3 maximum values
4 // Using Long.MIN_VALUE to handle edge cases with Integer.MIN_VALUE in the array
5 long firstMax = Long.MIN_VALUE;
6 long secondMax = Long.MIN_VALUE;
7 long thirdMax = Long.MIN_VALUE;
8
9 // Iterate through all numbers in the array
10 for (int num : nums) {
11 // Skip duplicates - we only want distinct maximum values
12 if (num == firstMax || num == secondMax || num == thirdMax) {
13 continue;
14 }
15
16 // Update the three maximum values based on current number
17 if (num > firstMax) {
18 // Current number is the new largest, shift all values down
19 thirdMax = secondMax;
20 secondMax = firstMax;
21 firstMax = num;
22 } else if (num > secondMax) {
23 // Current number is the new second largest
24 thirdMax = secondMax;
25 secondMax = num;
26 } else if (num > thirdMax) {
27 // Current number is the new third largest
28 thirdMax = num;
29 }
30 }
31
32 // If third maximum exists, return it; otherwise return the first maximum
33 // thirdMax will remain Long.MIN_VALUE if there are fewer than 3 distinct numbers
34 return (int) (thirdMax != Long.MIN_VALUE ? thirdMax : firstMax);
35 }
36}
37
1class Solution {
2public:
3 int thirdMax(vector<int>& nums) {
4 // Initialize three variables to track the top 3 maximum values
5 // Using LONG_MIN to handle edge cases where array contains INT_MIN
6 long firstMax = LONG_MIN;
7 long secondMax = LONG_MIN;
8 long thirdMax = LONG_MIN;
9
10 // Iterate through all numbers in the array
11 for (int num : nums) {
12 // Skip duplicates - we only want distinct maximum values
13 if (num == firstMax || num == secondMax || num == thirdMax) {
14 continue;
15 }
16
17 // Update the three maximum values based on current number
18 if (num > firstMax) {
19 // Current number is the new maximum
20 // Shift all values down: first -> second -> third
21 thirdMax = secondMax;
22 secondMax = firstMax;
23 firstMax = num;
24 } else if (num > secondMax) {
25 // Current number is the new second maximum
26 // Shift second -> third
27 thirdMax = secondMax;
28 secondMax = num;
29 } else if (num > thirdMax) {
30 // Current number is the new third maximum
31 thirdMax = num;
32 }
33 }
34
35 // Return third maximum if it exists, otherwise return the maximum
36 // If thirdMax is still LONG_MIN, it means we have less than 3 distinct numbers
37 return static_cast<int>(thirdMax != LONG_MIN ? thirdMax : firstMax);
38 }
39};
40
1function thirdMax(nums: number[]): number {
2 // Initialize three variables to track the top 3 maximum values
3 // Using -Infinity to handle edge cases where array contains minimum integer values
4 let firstMax: number = -Infinity;
5 let secondMax: number = -Infinity;
6 let thirdMax: number = -Infinity;
7
8 // Iterate through all numbers in the array
9 for (const num of nums) {
10 // Skip duplicates - we only want distinct maximum values
11 if (num === firstMax || num === secondMax || num === thirdMax) {
12 continue;
13 }
14
15 // Update the three maximum values based on current number
16 if (num > firstMax) {
17 // Current number is the new maximum
18 // Shift all values down: first -> second -> third
19 thirdMax = secondMax;
20 secondMax = firstMax;
21 firstMax = num;
22 } else if (num > secondMax) {
23 // Current number is the new second maximum
24 // Shift second -> third
25 thirdMax = secondMax;
26 secondMax = num;
27 } else if (num > thirdMax) {
28 // Current number is the new third maximum
29 thirdMax = num;
30 }
31 }
32
33 // Return third maximum if it exists, otherwise return the maximum
34 // If thirdMax is still -Infinity, it means we have less than 3 distinct numbers
35 return thirdMax !== -Infinity ? thirdMax : firstMax;
36}
37
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm iterates through the array exactly once, and for each element, it performs a constant number of operations - checking if the number exists in the three tracked values and comparing/updating at most three variables.
The space complexity is O(1)
. The algorithm only uses three variables (m1
, m2
, m3
) to track the three maximum values regardless of the input size. No additional data structures are created that scale with the input size.
Common Pitfalls
1. Incorrect Duplicate Check Using in
Operator with Floats
The most significant pitfall in this solution is using the in
operator to check if num
exists in a list containing float('-inf')
:
if num in [first_max, second_max, third_max]: continue
Why it's problematic:
- When the variables are still
-inf
, this creates a list like[float('-inf'), float('-inf'), float('-inf')]
- The
in
operator performs equality checks, which can be unreliable with floating-point infinity values - This approach also creates a new list on every iteration, adding unnecessary overhead
Solution: Use explicit comparisons instead:
# Check for duplicates explicitly if num == first_max or num == second_max or num == third_max: continue
2. Integer Overflow Concerns in Other Languages
While Python handles arbitrarily large integers, translating this solution to languages like Java or C++ requires careful consideration:
Problem: Using Integer.MIN_VALUE
as initial values won't work if the array contains Integer.MIN_VALUE
itself.
Solution for other languages:
Use null
values or a separate boolean flag:
# Alternative approach using None first_max = second_max = third_max = None for num in nums: # Skip duplicates if first_max == num or second_max == num or third_max == num: continue if first_max is None or num > first_max: third_max, second_max, first_max = second_max, first_max, num elif second_max is None or num > second_max: third_max, second_max = second_max, num elif third_max is None or num > third_max: third_max = num return third_max if third_max is not None else first_max
3. Edge Case: Array Contains -inf
If the input array could theoretically contain float('-inf')
as a valid element, the current solution would incorrectly handle it.
Solution: Use a different sentinel value or track count of distinct values:
def thirdMax(self, nums: List[int]) -> int:
# Use a set to get distinct values first
distinct = set(nums)
if len(distinct) < 3:
return max(distinct)
# Remove max twice to get third max
distinct.remove(max(distinct))
distinct.remove(max(distinct))
return max(distinct)
This approach is cleaner but uses O(n) space instead of O(1).
How does merge sort divide the problem into subproblems?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donโt Miss This!