最长连续序列
题目
给定一个未排序的整数数组 nums,找出数字连续的最长序列的长度。这里的”连续”不要求序列元素在原数组中相邻,并且题目要求时间复杂度为 O(n)。
示例
输入:nums = [100, 4, 200, 1, 3, 2]
输出:4
解释:最长连续序列是 [1, 2, 3, 4],长度为 4。它由原数组中的 1、3、2、4 四个数构成,顺序无关紧要。
思路
先观察一下问题:我们要找的是”连续的数字段”。例如 1、2、3、4 是一段,100 单独是一段,200 单独是一段。数出一段有多长,本质上是在反复做一件事:判断某个数的下一个数在不在数组里。
如果每次判断都去数组里线性扫描,代价是 O(n),整体会到 O(n²)。为了把这个判断变成 O(1),先把全部数字放进一个哈希集合 unordered_set,它同时做了两件事:
- 去重。重复出现的数字不影响答案,集合天然去重。
- 让”判断 x 在不在”成为平均 O(1) 的查询。
集合建好后,遍历每个数 x:
- 如果
x - 1已经在集合里,说明 x 不是某个连续段的开头,跳过它。因为这一段会由更小的那个开头去统计,不必重复劳动。 - 如果
x - 1不在集合里,说明 x 是一个连续段的开头。从 x 开始向上数:x、x+1、x+2……直到某个数不在集合中,这一段就到头了,记录长度。 - 每次统计完,更新目前遇到的最大长度。
这样每个连续段只被统计一次,不会超时。
代码
1 | class Solution { |
复杂度分析
设数组长度为 n。
- 时间复杂度:建集合 O(n);外层循环每个数一次判断 O(n);所有 while 合计最多 n 步(每个数只可能被当作”段首”数一次,或作为段内后继被访问一次)。整体 O(n)。
- 空间复杂度:集合存放全部数字,O(n)。
为什么不用排序
排序后相邻比较也能找出最长连续段,但排序本身是 O(n·log n),不满足题目 O(n) 的硬性要求。哈希集合的代价是 O(n) 空间,换来的是 O(1) 的存在性判断,这是典型的用空间换时间。
两个容易忽略的点
- “只从段首数”这一步不能省。 如果去掉”x−1 存在就跳过”的剪枝,对每个数都盲目向上数,最坏会退化成 O(n²)。这个剪枝正是把整体控制在 O(n) 的原因。
- 遍历集合本身即可。 遍历
st和遍历原数组结果一致,还天然去重;数组里的重复数字不会影响答案。
这类”判断元素是否存在”并要求 O(1) 的题目,第一选择是哈希集合。再加上”只从连续段起点统计”的剪枝,就同时保证了正确性和 O(n) 的复杂度。思路不复杂,代码也很短,难的是想清楚为什么不会超时。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 山川不念旧!
评论


