LeetCode 414 - 第三大的数

 
文章目录
- 摘要
 - 描述
 - 题解答案
 - 题解代码分析
 - 代码逐行讲解
 
- 示例测试及结果
 - 运行结果:
 
- 时间复杂度
 - 空间复杂度
 - 总结
 
摘要
这道题看起来挺简单的,但其实暗藏小坑:我们要找的是不同数字中的第三大数,而不是简单排序后的第三个数。比如 [2, 2, 3, 1] 这种情况就特别容易出错。
 本文会带你一步步分析解题思路,并提供一个可运行的 Swift Demo,帮你彻底搞清楚逻辑。

描述
题目要求我们:
 给定一个非空整数数组,返回数组中第三大的不同数字;如果不存在第三大,就返回最大的数。
具体规则:
- 数字要去重;
 - 如果去重后数量不足 3 个,就直接返回最大的;
 - 如果足够 3 个,就返回第三大的那个。
 
示例:
输入: [3, 2, 1]
输出: 1
 
输入: [1, 2]
输出: 2
 
输入: [2, 2, 3, 1]
输出: 1
 
这题还有一个进阶要求:
你能用 O(n) 的时间复杂度解决吗?
也就是说不能用 sort() 直接排序后取值,而是要通过遍历一次数组来得到结果。
题解答案
核心思路其实就是模拟“前三名”的评比过程:
-  
准备三个变量:
first,second,third,分别代表第一、第二、第三大; -  
遍历数组,每来一个数字:
- 如果和已有的三个重复,就跳过;
 - 否则按大小顺序更新前三名;
 
 -  
最后,如果
third存在,返回它,否则返回first。 
这个过程只需一次遍历,时间复杂度是 O(n),非常高效。

题解代码分析
来看完整的 Swift 实现
import Foundationclass Solution {func thirdMax(_ nums: [Int]) -> Int {// 初始化三个最大值变量为 nilvar first: Int? = nilvar second: Int? = nilvar third: Int? = nilfor num in nums {// 如果出现重复数字,直接跳过if num == first || num == second || num == third {continue}// 更新三个最大值if first == nil || num > first! {third = secondsecond = firstfirst = num} else if second == nil || num > second! {third = secondsecond = num} else if third == nil || num > third! {third = num}}// 判断是否存在第三大的数return third ?? first!}
}
 
代码逐行讲解
first,second,third都是可选类型,用来表示当前找到的前三个不同的最大数;- 遍历 
nums数组时,用三个if块依次判断是否需要更新; - 如果 
num比当前的最大数还大,就把现有最大往后挪; - 每次都小心处理 
nil,防止越界; - 最后如果 
third没有被赋值,说明不同的数字少于 3 个,直接返回first!(最大值)。 
这种方法比用 Set + sort() 更高效,因为只遍历一次。
示例测试及结果
来个可运行的测试代码:
let solution = Solution()print(solution.thirdMax([3, 2, 1]))        // 输出:1
print(solution.thirdMax([1, 2]))           // 输出:2
print(solution.thirdMax([2, 2, 3, 1]))     // 输出:1
print(solution.thirdMax([1, 2, 2, 5, 3, 5])) // 输出:2
 
运行结果:
1
2
1
2
 
结果符合预期。
时间复杂度
这段代码只遍历一次数组,更新三个变量,因此时间复杂度是:
O(n)
 
比起先 Set 去重再排序的 O(n log n) 效率高得多,满足进阶要求。
空间复杂度
只用了三个变量 first, second, third 来记录状态,空间复杂度是:
O(1)
 
非常轻量,不管数组多大都一样。
总结
这题看似简单,其实考察的是对“去重”和“排名逻辑”的处理。
 很多人第一次会直接用 sort() 来找第三大,但那样时间复杂度高、而且容易忽略重复值。
更优雅的思路是用三个变量维护前三名,遍历一次就能拿到结果。
 这类问题其实很常见,比如“找第二大”、“前三高分选手”等,都是相同逻辑。
