Problem overview
The method secondLargest(_ A: Int, _ B: Int, _ C: Int) -> Int in the class Solution receives three integers and returns the one that is second largest. For 7, 3, 9 the answer is 7, because 9 is the largest and 3 the smallest. For 12, 8, 3 the answer is 8. The method must take constant time and constant extra space.
Approach: test each candidate
You could sort the three numbers and pick the middle one, but sorting needs an array and is more work than three numbers deserve. A direct approach is to ask of each number whether it is the middle one.
A number is the second largest when it lies between the other two, ends included. For A, that means the smaller of B and C is at most A, and A is at most the larger of B and C. In symbols: min(B, C) <= A && A <= max(B, C). The same question for B uses A and C.
If neither A nor B is in the middle, C must be. No third test is needed.
Step by step
- Test
A. Ifmin(B, C) <= AandA <= max(B, C), returnA. Swift's globalminandmaxfunctions take two values of the same type. - Test
B. The same check againstAandC: ifmin(A, C) <= BandB <= max(A, C), returnB. - Return
C. Reaching this line meansAandBwere not in the middle, soCis.
The solution in Swift
class Solution {
func secondLargest(_ A: Int, _ B: Int, _ C: Int) -> Int {
if min(B, C) <= A && A <= max(B, C) {
return A
}
if min(A, C) <= B && B <= max(A, C) {
return B
}
return C
}
}
Details worth knowing:
- Global functions.
minandmaxare called without a receiver, as inmin(B, C). They work for any type that can be compared, includingInt. - Chained comparisons. Swift has no
x <= A <= yform, which is why the check is written as two comparisons joined with&&. - Parentheses. Comparisons bind tighter than
&&, so none are needed around each one. - Capitalised parameters.
A,BandCare ordinary parameter names. Swift style usually prefers lowercase, but the lesson's method uses these names, so the article does too.
Tracing an example
Take secondLargest(12, 8, 3). For A = 12, min(8, 3) is 3, which is at most 12, but 12 is not at most max(8, 3), which is 8. The first test fails. For B = 8, min(12, 3) is 3, which is at most 8, and 8 is at most max(12, 3), which is 12. The second test passes, so the method returns 8. For secondLargest(7, 3, 9), the first test passes immediately, since 3 is at most 7 and 7 is at most 9.
Complexity
Time: O(1). The method makes at most a fixed number of comparisons and calls to min and max. Space: O(1). It uses no extra storage.
Edge cases and common mistakes
- Ties. The comparisons are inclusive. For
5, 5, 2, the first test passes becausemin(5, 2)is 2, which is at most 5, and 5 is at mostmax(5, 2), which is 5. The method returns5. With4, 4, 4it returns4. - Strict comparisons. Using
<instead of<=would reject any middle value equal to a neighbour, and the method would returnCwrongly. - Mixing up which pair to compare. The check for
Bmust useAandC, notBitself.
Testing
let s = Solution()
print(s.secondLargest(7, 3, 9))
print(s.secondLargest(12, 8, 3))
print(s.secondLargest(5, 5, 2))
print(s.secondLargest(4, 4, 4))
print(s.secondLargest(1, 2, 3))
The output is:
7
8
5
4
2
