跳到正文
第 128 题中等

最长连续序列

Longest Consecutive Sequence

给定未排序的整数数组,求数字连续的最长序列的长度。序列不要求在数组中相邻,但要求 O(n)。

在力扣中练习

题目描述

给定一个未排序的整数数组 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

示例 3:

输入:nums = [1,0,1,2]
输出:3

 

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109

解题思路

主解法

哈希集合 · 只从起点数

把「查找」降到 O(1),再让每段序列只被数一次

先排序也能找连续段,但要付出 O(n log n)。哈希集合能去重并查询某个值是否存在,关键是避免从一段里的每一个数重新往后数。

自拟主例 [8,2,5,3,2,9,4,12] 有重复值,也有多个互不相连的段。去重后最长的是 2,3,4,5,长度为 4;8,9 和单独的 12 不会与它拼起来。

分步图解

从起点 8 扩展到 9,长度 2,best=2。

best
2
原数组 · 重复的 2 只算一次
0
1
2
3
4
5
6
7
8
2
5
3
2
9
4
12
集合(排序仅为读图;绿色为当前最长段)
23458912
1 / 4
只有没有前驱的值才展开整段,重复值先由集合消除。

只有 x-1 不存在时,x 才是起点。 从这个起点递增到整段末尾,更新最长长度。其他段内元素只做一次前驱判断,不再展开。图中集合排序只为方便读图,代码不依赖集合的迭代顺序;从哪个起点先开始都得到同一个答案。

时间 平均 O(n)空间 O(n)

易错点

  • 不判断起点(不查 x - 1),答案虽然对,但最坏情况会退化成 O(n²)。
  • 长度写成 y - x,少了 1;或者内层数完忘了更新 best。
  • best 初始化为 1 却没处理空数组,nums = [] 时会返回 1(正确答案是 0)。
对照解法

排序 · 一次扫描

排好序后,连续的数一定挨在一起

结果正确,但 O(n log n) 不满足本题要求的 O(n);仅作排序思路对照,提交优先用主解法。

如果面试官不卡 O(n),最省事的做法是排序后扫一遍:相邻元素差 1 就 ++cur,差更多就 cur = 1 重新计数,相等则是重复元素,跳过。

它把「连续」这个关系变成了相邻位置的比较,所以不需要哈希表,排序还会使用 O(log n) 的递归栈。代价是排序的 O(n log n),以及会修改输入数组(如果不想改,先拷贝一份)。

这段代码有一个很容易踩的坑:重复元素必须先跳过再判差。否则 [1,2,2,3] 里第二个 2 会让 nums[i] - nums[i-1] == 0 走进 else 分支,把 cur 重置成 1。

两种写法对比着记:排序版思路直白,哈希版满足题目的 O(n) 要求。

  1. 1空数组直接返回 0。
  2. 2排序整个数组。
  3. 3从第二个元素开始扫描:相等就跳过(重复)。
  4. 4差 1 则 cur 加一,否则 cur 重置为 1,并随时更新 best。
时间 O(n log n)空间 O(log n)(排序递归栈)

易错点

  • 把重复元素的判断放到 else 分支里,cur 会被错误重置。
  • 忘记空数组,nums[0] 越界。

代码与演示

主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

演示输入nums = [8, 2, 5, 3, 2, 9, 4, 12]

用集合去重,best 从 0 开始。集合的遍历顺序不影响答案。

原数组 · 重复的 2 只算一次
0
1
2
3
4
5
6
7
8
2
5
3
2
9
4
12
集合(排序仅为读图;绿色为当前最长段)
23458912
1 / 9
查看 C++ 代码

延伸阅读

  • 「只从起点展开」是一种通用剪枝:先判断自己是不是最小/最左/最起点,再决定要不要花力气展开。
  • 数据范围小时也可以用并查集,把相邻的数合并,思路与哈希集合一致,只是合并时要靠哈希表找邻居。
  • 题目只要长度;如果要输出具体序列,在更新 best 时顺手把起点和终点记下来即可。