Facebook Pixel

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Any actual number in the array will be greater than -inf, ensuring proper updates
  2. 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:

  1. Skip duplicates: Check if num equals any of m1, m2, or m3. If it does, skip to the next iteration using continue. This ensures we only track distinct values.

  2. 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, return m3
  • 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 Evaluator

Example 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).

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

How does merge sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More