Problem
Given a non-empty array of integers, return the third maximum number in this array. If it does not exist, return the maximum number. The time complexity must be in O(n).
Example 1:
Input: [3, 2, 1]
Output: 1
Explanation: The third maximum is 1.
Example 2:
Input: [1, 2]
Output: 2
Explanation: The third maximum does not exist, so the maximum (2) is returned instead.
Example 3:
Input: [2, 2, 3, 1]
Output: 1
Explanation: Note that the third maximum here means the third maximum distinct number. Both numbers with value 2 are both considered as second maximum.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution {public : int thirdMax (vector <int >& nums) { int m1 = INT_MIN, m2 = INT_MIN, m3 = INT_MIN; for (int e : nums) { if (e > m1) { int tmp = m1; m1 = e; int tmp2 = m2; m2 = tmp; m3 = tmp2; } else if (e > m2) { int tmp = m2; m2 = e; m3 = tmp; } else if (e > m3) { m3 = e; } } if (m3 > INT_MIN) { return m3; } else if (m2 > INT_MIN) { return m2; } return m1; } };
Oops! Read the question carefully.
If it does not exist, return the maximum number.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : int thirdMax (vector <int >& nums) { int m1 = INT_MIN, m2 = INT_MIN, m3 = INT_MIN; for (int e : nums) { if (e > m1) { int tmp = m1; m1 = e; int tmp2 = m2; m2 = tmp; m3 = tmp2; } else if ((e > m2) && (e != m1)) { int tmp = m2; m2 = e; m3 = tmp; } else if ((e > m3) && (e != m2)) { m3 = e; } } if (m3 > INT_MIN) { return m3; } return m1; } };
There still a problem with this!
For [3,2,3]
we see m1 = 3, m2 = 3,m3 = 2
! 😱
We should have seen m1 = 3, m2 = 2,m3 = INT_MIN
!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {public : int thirdMax (vector <int >& nums) { int m1 = INT_MIN, m2 = INT_MIN, m3 = INT_MIN; for (int e : nums) { if (e > m1) { int tmp = m1; m1 = e; int tmp2 = m2; m2 = tmp; m3 = tmp2; } else if ((e > m2) && (e != m1)) { int tmp = m2; m2 = e; m3 = tmp; } else if ((e > m3) && (e != m2)) { m3 = e; } } if (m3 > INT_MIN) { return m3; } return m1; } };
Well this fails the testcase below.
1 2 3 Input: [1,2,2,5,3,5] Output: 5 Expected: 2
The reason is because we need to ensure we check both m1 and m2 !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : int thirdMax (vector <int >& nums) { int m1 = INT_MIN, m2 = INT_MIN, m3 = INT_MIN; for (int e : nums) { if (e > m1) { int tmp = m1; m1 = e; int tmp2 = m2; m2 = tmp; m3 = tmp2; } else if ((e > m2) && (e != m1)) { int tmp = m2; m2 = e; m3 = tmp; } else if ((e > m3) && (e != m2) && (e != m1)) { m3 = e; } } if (m3 > INT_MIN) { return m3; } return m1; } };
Oh great. Now it fails here… 🤕 Ah you got me there!
1 2 3 4 Input: [1,2,-2147483648] Output: 2 Expected: -2147483648
Well we have no way of knowing right now that the m3 was set to INT_MIN or was never updated. If we has used a data structure like a set this might have been simpler and cleaner.
Well we can possibly use a flag to denote when all variables have been touched and when they have not. Then we also need to test >=
as opposed to >
.
Attempt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution {public : int thirdMax (vector <int >& nums) { int m1 = INT_MIN, m2 = INT_MIN, m3 = INT_MIN; bool m3updated = false ; for (int e : nums) { if (e >= m1) { int tmp = m1; m1 = e; int tmp2 = m2; m2 = tmp; m3 = tmp2; } else if ((e >= m2) && (e != m1)) { int tmp = m2; m2 = e; m3 = tmp; } else if ((e >= m3) && (e != m2) && (e != m1)) { m3 = e; m3updated = true ; } } if (m3 > INT_MIN) { return m3; } return m1; } };
This is not a good approach.
Let’s go back and think about the requirement a bit more. Well the numbers are int
s. So we cannot initialize to INT_MIN
. 🤔
OK so what can we initialize this to?
How about LONG_MIN
💡!
Accepted 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution {public : int thirdMax (vector <int >& nums) { long m1 = LONG_MIN, m2 = LONG_MIN, m3 = LONG_MIN; for (int e : nums) { if ((long )e > m1) { long tmp = m1; m1 = (long ) e; long tmp2 = m2; m2 = tmp; m3 = tmp2; } else if (((long )e > m2) && ((long )e != m1)) { long tmp = m2; m2 = (long )e; m3 = tmp; } else if (((long )e > m3) && ((long )e != m2) && ((long )e != m1)) { m3 = e; } } if (m3 > LONG_MIN) { return m3; } return m1; } };
Nice 👍
But this has an obvious shortfall. What if the inputs are long
? Well we can use long long
?
But you see the limitations.
A set would be cleaner.