|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[414. 第三大的数](https://leetcode-cn.com/problems/third-maximum-number/solution/gong-shui-san-xie-yi-ti-shuang-jie-pai-x-pmln/)** ,难度为 **简单**。 |
| 4 | + |
| 5 | +Tag : 「排序」、「数组」、「模拟」 |
| 6 | + |
| 7 | +给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。 |
| 8 | + |
| 9 | +示例 1: |
| 10 | +``` |
| 11 | +输入:[3, 2, 1] |
| 12 | + |
| 13 | +输出:1 |
| 14 | + |
| 15 | +解释:第三大的数是 1 。 |
| 16 | +``` |
| 17 | +示例 2: |
| 18 | +``` |
| 19 | +输入:[1, 2] |
| 20 | + |
| 21 | +输出:2 |
| 22 | + |
| 23 | +解释:第三大的数不存在, 所以返回最大的数 2 。 |
| 24 | +``` |
| 25 | +示例 3: |
| 26 | +``` |
| 27 | +输入:[2, 2, 3, 1] |
| 28 | + |
| 29 | +输出:1 |
| 30 | + |
| 31 | +解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。 |
| 32 | +此例中存在两个值为 2 的数,它们都排第二。在所有不同数字中排第三大的数为 1 。 |
| 33 | +``` |
| 34 | + |
| 35 | +提示: |
| 36 | +* 1 <= nums.length <= 10ドル^4$ |
| 37 | +* $-2^{31}$ <= nums[i] <= 2ドル^{31} - 1$ |
| 38 | + |
| 39 | +**进阶:你能设计一个时间复杂度 O(n) 的解决方案吗?** |
| 40 | + |
| 41 | +--- |
| 42 | + |
| 43 | +### Set 去重 + 排序 |
| 44 | + |
| 45 | +题目要求返回含重复元素的数组 $nums$ 中的第三大数。 |
| 46 | + |
| 47 | +一个朴素的做法是,先使用 `Set` 对重复元素进行去重,然后对去重后的元素进行排序,并返回第三大的元素。 |
| 48 | + |
| 49 | +代码: |
| 50 | +```Java |
| 51 | +class Solution { |
| 52 | + public int thirdMax(int[] nums) { |
| 53 | + Set<Integer> set = new HashSet<>(); |
| 54 | + for (int x : nums) set.add(x); |
| 55 | + List<Integer> list = new ArrayList<>(set); |
| 56 | + Collections.sort(list); |
| 57 | + return list.size() < 3 ? list.get(list.size() - 1) : list.get(list.size() - 3); |
| 58 | + } |
| 59 | +} |
| 60 | +``` |
| 61 | +* 时间复杂度:使用 `Set` 去重的复杂度为 $O(n)$;排序复杂度为 $O(n\log{n})$。整体复杂度为 $O(n\log{n})$ |
| 62 | +* 空间复杂度:$O(n)$ |
| 63 | + |
| 64 | +--- |
| 65 | + |
| 66 | +### 有限变量 + 遍历 |
| 67 | + |
| 68 | +**经典的找数组次大值的做法是使用两个变量 `a` 和 `b` 分别存储遍历过程中的最大值和次大值。** |
| 69 | + |
| 70 | +假设当前遍历到的元素为 $x,ドル当满足如下条件时,考虑更新 `a` 或者 `b`: |
| 71 | +1. 当 $x > a$ 时,说明最大值被更新,同时原来的最大值沦为次大值。即有 $b = a; a = x;$ |
| 72 | +2. 在条件 1ドル$ 不满足,且有$x > b$ 时,此时可以根据是否有「严格次大值」的要求,而决定是否要增加 $x < a$ 的条件: |
| 73 | + * 不要求为「严格次大值」:直接使用 $x$ 来更新 `b`,即有 $b = x$; |
| 74 | + * 当要求为「严格次大值」: 此时需要满足 $x < a$ 的条件,才能更新 `b`。 |
| 75 | + |
| 76 | +回到本题,同理我们可以使用 `a`、`b` 和 `c` 三个变量来代指「最大值」、「严格次大值」和「严格第三大值」。 |
| 77 | + |
| 78 | +从前往后遍历 $nums,ドル假设当前元素为 $x,ドル对是否更新三者进行分情况讨论(判断优先级从上往下): |
| 79 | + |
| 80 | +1. $x > a,ドル说明最大值被更新,将原本的「最大值」和「次大值」往后顺延为「次大值」和「第三大值」,并用 $x$ 更新 `a`; |
| 81 | +2. $x < a$ 且 $x > b,ドル说明次大值被更新,将原本的「次大值」往后顺延为「第三大值」,并用 $x$ 更新 `b`; |
| 82 | +3. $x < b$ 且 $x > c,ドル说明第三大值被更新,使用 $x$ 更新 `c`。 |
| 83 | + |
| 84 | +起始时,我们希望使用一个足够小的数来初始化 `a`、`b` 和 `c`,但由于 $num[i]$ 的范围为 $[-2^{31}, 2^{31} - 1],ドル因此需要使用 `long` 来进行代替。 |
| 85 | + |
| 86 | +返回时,通过判断第三大值是否为初始化时的负无穷,来得知是否存在第三大值。 |
| 87 | + |
| 88 | +代码: |
| 89 | +```Java |
| 90 | +class Solution { |
| 91 | + long INF = (long)-1e18; |
| 92 | + public int thirdMax(int[] nums) { |
| 93 | + long a = INF, b = INF, c = INF; |
| 94 | + for (int x : nums) { |
| 95 | + if (x > a) { |
| 96 | + c = b; b = a; a = x; |
| 97 | + } else if (x < a && x > b) { |
| 98 | + c = b; b = x; |
| 99 | + } else if (x < b && x > c) { |
| 100 | + c = x; |
| 101 | + } |
| 102 | + } |
| 103 | + return c != INF ? (int)c : (int)a; |
| 104 | + } |
| 105 | +} |
| 106 | +``` |
| 107 | +* 时间复杂度:$O(n)$ |
| 108 | +* 空间复杂度:$O(1)$ |
| 109 | + |
| 110 | +--- |
| 111 | + |
| 112 | +### 最后 |
| 113 | + |
| 114 | +这是我们「刷穿 LeetCode」系列文章的第 `No.414` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 115 | + |
| 116 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 117 | + |
| 118 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 119 | + |
| 120 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 121 | + |
0 commit comments