题目描述
给定一个未排序的整数数组 nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n)
的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
解法
方法一:哈希表
我们可以用一个哈希表 $\textit{s}$ 存储数组中所有的元素,用一个变量 $\textit{ans}$ 记录最长连续序列的长度,用一个哈希表 $\textit{d}$ 记录每个元素 $x$ 所在的连续序列的长度。
接下来,我们遍历数组中每个元素 $x$,用一个临时变量 $y$ 记录当前连续序列的最大值,初始时 $y = x$。然后,我们不断尝试匹配 $y+1, y+2, y+3, \dots$,直到匹配不到为止,过程中将匹配到的元素从哈希表 $\textit{s}$ 中移除。那么,当前元素 $x$ 所在的连续序列的长度即为 $d[x] = d[y] + y - x$,然后更新答案 $\textit{ans} = \max(\textit{ans}, d[x])$。
遍历结束后,返回答案 $\textit{ans}$ 即可。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是数组 $\textit{nums}$ 的长度。
1
2
3
4
5
6
7
8
9
10
11
12
13 | class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
s = set(nums)
ans = 0
d = defaultdict(int)
for x in nums:
y = x
while y in s:
s.remove(y)
y += 1
d[x] = d[y] + y - x
ans = max(ans, d[x])
return ans
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> s = new HashSet<>();
for (int x : nums) {
s.add(x);
}
int ans = 0;
Map<Integer, Integer> d = new HashMap<>();
for (int x : nums) {
int y = x;
while (s.contains(y)) {
s.remove(y++);
}
d.put(x, d.getOrDefault(y, 0) + y - x);
ans = Math.max(ans, d.get(x));
}
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end());
int ans = 0;
unordered_map<int, int> d;
for (int x : nums) {
int y = x;
while (s.contains(y)) {
s.erase(y++);
}
d[x] = (d.contains(y) ? d[y] : 0) + y - x;
ans = max(ans, d[x]);
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | func longestConsecutive(nums []int) (ans int) {
s := map[int]bool{}
for _, x := range nums {
s[x] = true
}
d := map[int]int{}
for _, x := range nums {
y := x
for s[y] {
delete(s, y)
y++
}
d[x] = d[y] + y - x
ans = max(ans, d[x])
}
return
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | function longestConsecutive(nums: number[]): number {
const s = new Set(nums);
let ans = 0;
const d = new Map<number, number>();
for (const x of nums) {
let y = x;
while (s.has(y)) {
s.delete(y++);
}
d.set(x, (d.get(y) || 0) + (y - x));
ans = Math.max(ans, d.get(x)!);
}
return ans;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | use std::collections::{HashMap, HashSet};
impl Solution {
pub fn longest_consecutive(nums: Vec<i32>) -> i32 {
let mut s: HashSet<i32> = nums.iter().cloned().collect();
let mut ans = 0;
let mut d: HashMap<i32, i32> = HashMap::new();
for &x in &nums {
let mut y = x;
while s.contains(&y) {
s.remove(&y);
y += 1;
}
let length = d.get(&(y)).unwrap_or(&0) + y - x;
d.insert(x, length);
ans = ans.max(length);
}
ans
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | /**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function (nums) {
const s = new Set(nums);
let ans = 0;
const d = new Map();
for (const x of nums) {
let y = x;
while (s.has(y)) {
s.delete(y++);
}
d.set(x, (d.get(y) || 0) + (y - x));
ans = Math.max(ans, d.get(x));
}
return ans;
};
|
方法二:哈希表(优化)
与方法一类似,我们用一个哈希表 $\textit{s}$ 存储数组中所有的元素,用一个变量 $\textit{ans}$ 记录最长连续序列的长度。但是,我们不再使用哈希表 $\textit{d}$ 记录每个元素 $x$ 所在的连续序列的长度,在遍历的过程中,跳过那些 $x-1$ 也在哈希表 $\textit{s}$ 中的元素,如果 $x-1$ 在哈希表 $\textit{s}$ 中,那么 $x$ 一定不是连续序列的起点,因此我们可以直接跳过 $x$。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是数组 $\textit{nums}$ 的长度。
| class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
s = set(nums)
ans = 0
for x in s:
if x - 1 not in s:
y = x + 1
while y in s:
y += 1
ans = max(ans, y - x)
return ans
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> s = new HashSet<>();
for (int x : nums) {
s.add(x);
}
int ans = 0;
for (int x : s) {
if (!s.contains(x - 1)) {
int y = x + 1;
while (s.contains(y)) {
++y;
}
ans = Math.max(ans, y - x);
}
}
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end());
int ans = 0;
for (int x : s) {
if (!s.contains(x - 1)) {
int y = x + 1;
while (s.contains(y)) {
y++;
}
ans = max(ans, y - x);
}
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | func longestConsecutive(nums []int) (ans int) {
s := map[int]bool{}
for _, x := range nums {
s[x] = true
}
for x, _ := range s {
if !s[x-1] {
y := x + 1
for s[y] {
y++
}
ans = max(ans, y-x)
}
}
return
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | function longestConsecutive(nums: number[]): number {
const s = new Set<number>(nums);
let ans = 0;
for (const x of s) {
if (!s.has(x - 1)) {
let y = x + 1;
while (s.has(y)) {
y++;
}
ans = Math.max(ans, y - x);
}
}
return ans;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | use std::collections::HashSet;
impl Solution {
pub fn longest_consecutive(nums: Vec<i32>) -> i32 {
let s: HashSet<i32> = nums.iter().cloned().collect();
let mut ans = 0;
for &x in &s {
if !s.contains(&(x - 1)) {
let mut y = x + 1;
while s.contains(&y) {
y += 1;
}
ans = ans.max(y - x);
}
}
ans
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | /**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function (nums) {
const s = new Set(nums);
let ans = 0;
for (const x of nums) {
if (!s.has(x - 1)) {
let y = x + 1;
while (s.has(y)) {
y++;
}
ans = Math.max(ans, y - x);
}
}
return ans;
};
|