Problem overview
The method secondLargest receives a list nums of integers and returns the second largest distinct value. The input is guaranteed to contain at least two distinct values.
In the example, nums = [2, 7, 11, 8, 11, 8, 3, 11]. The largest value is 11, which appears three times. The next distinct value below it is 8, so the answer is 8, not 11. A second copy of the maximum does not count as the second largest.
Approach: track the top two in a single pass
The obvious solution sorts the list in descending order and picks the second item, but duplicates make it awkward, and sorting costs O(n log n). The statement's follow-up asks for one pass without sorting.
You can do it by remembering only two values while you walk through the list: the largest seen so far and the runner-up. Each new number does one of three things:
- It beats the current maximum. The old maximum is now the runner-up, and the new number is the maximum.
- It is smaller than the maximum but larger than the runner-up. It becomes the new runner-up.
- Otherwise, it changes nothing: it is equal to the maximum, or no better than the runner-up.
Both variables start at negative infinity, so any real number beats them and the first iteration needs no special case. Negative infinity is also a good starting point for lists of negative numbers, which a starting value of 0 would handle wrongly.
Here is the example traced. The pair is written as (max_val, second):
- Start: (-inf, -inf).
- 2 beats the maximum: (2, -inf).
- 7 beats it: (7, 2).
- 11 beats it: (11, 7).
- 8 is below 11 and above 7: (11, 8).
- 11 equals the maximum, so nothing changes. The same is true for the last 11.
- The remaining 8 and 3 are not above the runner-up 8, so nothing changes.
The result is 8.
Step by step
- Declare
max_valandsecond, both asfloat('-inf'). This value compares smaller than every integer. - Loop over
numswith the variablenum. - Test for a new maximum with
num > max_val. Use a strict comparison. - Demote, then replace. Inside that branch, first copy
max_valintosecond, and only then storenuminmax_val. The order matters, because after the second assignment the old maximum is lost. - Test for a new runner-up in an
elif:nummust be belowmax_valand abovesecond. The first condition keeps copies of the maximum out of second place, and the second keeps a worse number from replacing a better runner-up. - Update
secondtonumin that branch. - Return
secondafter the loop.
The solution in Python
class Solution:
def secondLargest(self, nums: List[int]) -> int:
max_val = float('-inf')
second = float('-inf')
for num in nums:
if num > max_val:
second = max_val
max_val = num
elif num < max_val and num > second:
second = num
return second
The Python details worth a closer look:
float('-inf')works with integers. Python compares anintand a float directly, so5 > float('-inf')isTrue. No conversion is needed.- The return value can be a float. If the input had fewer than two distinct values,
secondwould still be-infwhen the loop ends. The guarantee in the statement rules this out, so for valid input the function returns an integer. - Chained
elif. Theelifruns only when theifcondition was false, so a number never updates both variables in the same iteration. - Why not a swap trick? Python allows
second, max_val = max_val, numin one line, which also gets the order right, but two separate assignments make the "copy first" rule visible. - Why not
sorted(nums)[-2]? With duplicates of the maximum, the second-to-last item of the sorted list is the maximum again. Usingsorted(set(nums))[-2]fixes that, at the cost of a sort and an extra set.
Complexity
Time: O(n). The loop visits each element once and does a constant number of comparisons.
Space: O(1). Only two variables are kept, whatever the size of the list.
Edge cases and common mistakes
- Duplicates of the maximum. For
[5, 5, 1]the answer is 1. The testnum < max_valstops the second 5 from becoming the runner-up. - All negative numbers. For
[-3, -1, -2]the answer is -2. Starting both variables at 0 would give a wrong answer here, which is why negative infinity is used. - Only two elements.
[1, 2]returns 1. - Ascending or descending input. Both are fine. In ascending order the runner-up is replaced by every new maximum, and in descending order it is set by the second element.
- Overwriting in the wrong order. Setting
max_val = numbeforesecond = max_valmakes both variables equal tonum. - Using
>=in the first test. A repeated maximum would then copy the maximum intosecond, which returns the maximum itself. - No second distinct value.
[3, 3]would return-inf. The statement promises this never happens.
Testing
[2, 7, 11, 8, 11, 8, 3, 11]gives8.[5, 5, 1]gives1.[-3, -1, -2]gives-2.[1, 2]gives1.