0128.最长连续序列
难度:🟡 中等
标签:数组
、哈希表
、并查集
链接:128. 最长连续序列
题目描述
给定一个未排序的整数数组 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
解题思路
核心思想
本题要求以 O(n) 的时间复杂度找出最长的连续序列。这排除了需要 O(n log n) 的排序算法。核心思想是利用哈希数据结构实现快速查找,从而避免不必要的重复计算。
思路选择
哈希集合(Set)法 是解决此问题的标准最优思路。
我们可以先将所有数组元素存入一个哈希集合
Set
中。这有两个好处:一是自动去重,二是提供了 O(1) 的平均时间复杂度来查找一个元素是否存在。然后,我们遍历这个哈希集合。对于集合中的每一个数
num
,我们判断它是否是一个连续序列的起点。一个数num
是起点的充要条件是num - 1
不在集合中。如果
num
是一个起点,我们就从这个数开始,不断地检查num + 1
,num + 2
, ... 是否存在于集合中,以此来计算当前连续序列的长度,并更新全局的最大长度。
关键步骤
处理边界情况:如果数组为空,直接返回 0。
初始化集合:创建一个哈希集合
numSet
,并将nums
的所有元素存入。初始化最大长度:
maxLength = 0
。遍历集合:遍历
numSet
中的每一个数字num
。寻找序列起点:对于每个
num
,检查numSet
中是否存在num - 1
。如果存在,说明num
不是序列的起点,直接跳过。计算序列长度:如果
num - 1
不存在,说明我们找到了一个序列的起点。从这个点开始,通过一个while
循环不断检查num + 1
,num + 2
, ... 是否存在于numSet
中,并记录下当前序列的长度currentLength
。更新最大值:用
currentLength
更新maxLength
。返回结果:遍历结束后,
maxLength
就是最长连续序列的长度。
代码实现
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function(nums) {
if (nums.length === 0) {
return 0;
}
const numSet = new Set(nums);
let maxLength = 0;
for (const num of numSet) {
// 只有当 num-1 不存在时,才把 num 当作序列的起点,避免重复计算
if (!numSet.has(num - 1)) {
let currentNum = num;
let currentLength = 1;
// 从起点开始,计算当前连续序列的长度
while (numSet.has(currentNum + 1)) {
currentNum++;
currentLength++;
}
// 更新最大长度
maxLength = Math.max(maxLength, currentLength);
}
}
return maxLength;
};
复杂度分析
时间复杂度:O(n) 其中 n 是数组
nums
的长度。虽然代码中有嵌套循环,但while
循环的每次执行都对应numSet
中一个未被内部循环访问过的元素。因此,每个元素最多被访问两次(一次在for
循环,一次在while
循环),总时间复杂度是线性的。空间复杂度:O(n) 我们需要一个哈希集合来存储所有不重复的元素,在最坏的情况下(所有元素都不同),空间消耗与 n 成正比。
相关题目
总结
本题是哈希表应用的经典案例。通过将问题转化为“寻找序列的起点”,并利用哈希表 O(1) 的查找特性,巧妙地将时间复杂度控制在线性级别,避免了排序带来的额外开销。