LC 414. 第三大的数

题目描述

这是 LeetCode 上的 414. 第三大的数 ,难度为 简单

给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。

示例 1:

1
2
3
4
5
输入:[3, 2, 1]

输出:1

解释:第三大的数是 1

示例 2:
1
2
3
4
5
输入:[1, 2]

输出:2

解释:第三大的数不存在, 所以返回最大的数 2

示例 3:
1
2
3
4
5
6
输入:[2, 2, 3, 1]

输出:1

解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
此例中存在两个值为 2 的数,它们都排第二。在所有不同数字中排第三大的数为 1

提示:

  • 1 <= nums.length <= $10^4$
  • $-2^{31}$ <= nums[i] <= $2^{31} - 1$

进阶:你能设计一个时间复杂度 O(n) 的解决方案吗?


Set 去重 + 排序

题目要求返回含重复元素的数组 $nums$ 中的第三大数。

一个朴素的做法是,先使用 Set 对重复元素进行去重,然后对去重后的元素进行排序,并返回第三大的元素。

代码:

1
2
3
4
5
6
7
8
9
class Solution {
public int thirdMax(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int x : nums) set.add(x);
List<Integer> list = new ArrayList<>(set);
Collections.sort(list);
return list.size() < 3 ? list.get(list.size() - 1) : list.get(list.size() - 3);
}
}

  • 时间复杂度:使用 Set 去重的复杂度为 $O(n)$;排序复杂度为 $O(n\log{n})$。整体复杂度为 $O(n\log{n})$
  • 空间复杂度:$O(n)$

有限变量 + 遍历

经典的找数组次大值的做法是使用两个变量 ab 分别存储遍历过程中的最大值和次大值。

假设当前遍历到的元素为 $x$,当满足如下条件时,考虑更新 a 或者 b:

  1. 当 $x > a$ 时,说明最大值被更新,同时原来的最大值沦为次大值。即有 $b = a; a = x;$
  2. 在条件 $1$ 不满足,且有$x > b$ 时,此时可以根据是否有「严格次大值」的要求,而决定是否要增加 $x < a$ 的条件:
    • 不要求为「严格次大值」:直接使用 $x$ 来更新 b,即有 $b = x$;
    • 当要求为「严格次大值」: 此时需要满足 $x < a$ 的条件,才能更新 b

回到本题,同理我们可以使用 abc 三个变量来代指「最大值」、「严格次大值」和「严格第三大值」。

从前往后遍历 $nums$,假设当前元素为 $x$,对是否更新三者进行分情况讨论(判断优先级从上往下):

  1. $x > a$,说明最大值被更新,将原本的「最大值」和「次大值」往后顺延为「次大值」和「第三大值」,并用 $x$ 更新 a
  2. $x < a$ 且 $x > b$,说明次大值被更新,将原本的「次大值」往后顺延为「第三大值」,并用 $x$ 更新 b
  3. $x < b$ 且 $x > c$,说明第三大值被更新,使用 $x$ 更新 c

起始时,我们希望使用一个足够小的数来初始化 abc,但由于 $num[i]$ 的范围为 $[-2^{31}, 2^{31} - 1]$,因此需要使用 long 来进行代替。

返回时,通过判断第三大值是否为初始化时的负无穷,来得知是否存在第三大值。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
long INF = (long)-1e18;
public int thirdMax(int[] nums) {
long a = INF, b = INF, c = INF;
for (int x : nums) {
if (x > a) {
c = b; b = a; a = x;
} else if (x < a && x > b) {
c = b; b = x;
} else if (x < b && x > c) {
c = x;
}
}
return c != INF ? (int)c : (int)a;
}
}

  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

最后

这是我们「刷穿 LeetCode」系列文章的第 No.414 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!