最长连续序列:我的哈希集合解法与优化思路

12 阅读4分钟

最长连续序列:哈希集合的巧妙运用

在力扣上面刷到的这道题目,提交了一直超时,才发现遍历了原数组,没有去重,这道题目的思路还是很重要的,所以,写一篇文章分享分享

问题分析

给定一个未排序的整数数组 nums,需要找出其中数字连续的最长序列的长度,并且要求时间复杂度为 O(n)。

举个例子:

输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]

我的解题思路

第一步:建立哈希集合去重

首先我想到要用哈希集合来存储数组的所有元素,这样做有两个好处:

  1. 快速查询某个数是否存在(O(1)时间复杂度)
  2. 自动去重,避免重复数字干扰
let cnt = new Set(nums);  // 去重 + 快速查询

第二步:关键优化——只从序列起点开始查找

这是我算法的核心优化点。如果直接遍历每个数都向后查找,会有大量重复计算。

我的观察:一个连续序列只需要从它的最小数开始查找就行了。
比如序列 [1, 2, 3, 4],从1开始查找可以得到整个序列,但如果从2、3、4开始查找,只会得到部分结果,是重复计算。

如何判断一个数是不是序列起点呢?
很简单:如果 num-1 不存在于集合中,那么 num 就是某个连续序列的起点

// 只有当前数是序列起点时才进行处理
if(!cnt.has(num-1)) {
    // 从起点开始向后扩展
}

第三步:从起点向后扩展计算长度

确定了起点后,就从这个数开始,不断检查 num+1num+2... 是否存在,直到遇到不存在的数字为止。

let sum = num;  // 从当前数开始
while(cnt.has(sum)) {
    sum++;  // 向后查找连续的数
}

第四步:计算并更新最大长度

当while循环结束时,sum 指向的是连续序列最后一个数的下一个值。
所以序列长度就是 sum - num

maxlen = Math.max(maxlen, sum - num);

完整代码实现

/**
 * @param {number[]} nums
 * @return {number}
 */
var longestConsecutive = function(nums) {
    // 1. 将数组转为哈希集合,自动去重
    let cnt = new Set(nums);
    let maxlen = 0;
    let sum = 0;
    
    // 2. 遍历集合(遍历集合而不是原数组,避免处理重复数)
    for(const num of cnt) {
        sum = num;
        
        // 关键优化:只有当前数是连续序列的起点时才处理,这样我们就避免了重复的计算
        // 判断标准:num-1 不在集合中
        if(!cnt.has(num-1)) {
            // 从起点开始向后查找连续的数
            while(cnt.has(sum)) {
                sum++;
            }
        }
        
        // 3. 更新最大长度
        // 此时 sum 是最后一个连续数的下一个值,所以长度 = sum - num
        maxlen = Math.max(maxlen, sum - num);
    }
    
    return maxlen;
};

算法执行示例

让我用 nums = [100, 4, 200, 1, 3, 2, 2] 来演示:

  1. 建立集合{100, 4, 200, 1, 3, 2}(去掉了重复的2)

  2. 遍历集合

    • 遇到100:99不存在 → 是起点
      向后找:101不存在 → 序列 [100],长度 = 1

    • 遇到4:3存在 → 不是起点 → 跳过

    • 遇到200:199不存在 → 是起点
      向后找:201不存在 → 序列 [200],长度 = 1

    • 遇到1:0不存在 → 是起点
      向后找:2存在,3存在,4存在,5不存在
      序列 [1, 2, 3, 4],长度 = 4

    • 遇到3:2存在 → 不是起点 → 跳过

    • 遇到2:1存在 → 不是起点 → 跳过

  3. 最终结果:4

时间复杂度分析

  • 每个元素最多被访问两次(一次在外层循环,一次在内层while)
  • 所有操作都是哈希集合的 O(1) 操作
  • 总时间复杂度:O(n)

为什么这个方法高效?

  1. 避免排序:排序需要 O(n log n),我们直接用 O(n)
  2. 避免重复计算:通过"只从起点开始"的优化,确保每个连续序列只被完整遍历一次
  3. 空间换时间:使用 O(n) 的额外空间(哈希集合)来获得 O(n) 的时间复杂度

总结

这道题的核心在于理解如何高效地找到连续序列。我的思路是:

  1. 用哈希集合去重 + 快速查询
  2. 只从序列的起点开始向后扩展
  3. 确保每个序列只被计算一次

这种"空间换时间"的思想在算法面试中非常常见,记住这个模式,可以解决很多类似的问题。