题目描述

✅ 128. 最长连续序列

image-20260928194458609

题意分析

在未排序数组中,找出数值连续的一段整数序列,返回它的最大长度。连续指相邻数值相差 1,不要求这些数在原数组中位置相邻,也不要求保留原数组的先后顺序。

同一个值出现多次只算一个,负数也可以参与连续序列。题目要求线性时间,不能依赖排序后扫描;需要快速判断某个值是否存在,并避免从同一段里的每个数都重新向后数一遍。空数组的答案为 0。

解法:哈希集合只从序列起点扩展

核心思路

[!blue]

先把所有数加入哈希集合。集合同时完成去重和快速存在性查询,原数组中的位置与出现次数便不再影响判断。接下来在集合中寻找每段连续序列的最小值,只从这些起点扩展。

对一个数 num,如果 num - 1 也在集合中,它就不是起点,所在连续段应该交给更小的数处理,当前直接跳过。如果前驱不存在,才从 num 开始依次查找 num + 1、再下一个值,直到首次缺失为止,得到这一整段的长度。

每一段连续整数恰好有一个没有前驱的最小值,所以只从起点搜索不会遗漏任何一段;同一段中的其他值又都会因存在前驱而跳过,不会重复展开。集合的遍历顺序不重要,因为是否为起点只取决于前驱是否存在,而不取决于谁先被访问。

外层也必须遍历去重后的集合,而不是原数组。否则同一个起点在输入中重复出现时,可能把整段序列反复展开,破坏线性时间。对每个互不相同的数,外层只检查一次前驱;所有向后扩展合起来也只跨过每段一次,因此总工作量是线性的。

用 ans 保存已处理连续段的最大长度。代码还在计算前驱和后继前检查整数极值,避免减一或加一溢出后,把整数两端错误地视为相邻。没有元素时不进入循环,初始的 0 就是答案。

解题步骤

  1. 将数组元素加入哈希集合,初始化 ans = 0。
  2. 遍历集合中的每个 num;若存在前驱 num - 1,跳过它。
  3. 对没有前驱的起点,初始化 cur = num、当前长度为 1。
  4. 只要后继 cur + 1 存在,就推进 cur 并增加长度;整数极值处先判断再做加减。
  5. 用这段长度更新 ans,遍历结束后返回最大值。

代码实现

class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> set = new HashSet<>();

        for (int num : nums) {
            set.add(num);
        }

        int ans = 0;

        // 遍历去重后的集合,重复起点不会反复展开同一条序列。
        for (int num : set) {
            // 存在前驱就不是序列起点,把扩展留给更小的起点。
            if (num != Integer.MIN_VALUE && set.contains(num - 1)) {
                continue;
            }

            int cur = num;
            int len = 1;

            while (cur != Integer.MAX_VALUE && set.contains(cur + 1)) {
                cur++;
                len++;
            }

            ans = Math.max(ans, len);
        }

        return ans;
    }
}
import "math"

func longestConsecutive(nums []int) int {
    set := make(map[int]bool)
    for _, num := range nums {
        set[num] = true
    }

    ans := 0
    // 遍历去重后的集合,重复起点不会反复展开同一条序列。
    for num := range set {
        // 存在前驱就不是序列起点,把扩展留给更小的起点。
        if num != math.MinInt && set[num-1] {
            continue
        }

        cur := num
        length := 1

        for cur != math.MaxInt && set[cur+1] {
            cur++
            length++
        }
        if length > ans {
            ans = length
        }
    }

    return ans
}

复杂度分析

  • 时间复杂度:平均 $O(n)$,哈希插入与查询平均为常数时间;外层检查每个不同值一次,所有连续段的扩展次数之和不超过不同值的总数。
  • 空间复杂度:$O(n)$,哈希集合最多保存数组中全部不同的值。

关键点总结

[!green]

  • 哈希集合同时提供快速查找和去重。
  • num - 1 不存在是连续序列起点的唯一判据。
  • 只从起点扩展,才能保证所有扩展步数合计为 $O(n)$。
  • 计算前驱和后继时要防止整数极值溢出,避免把最小值与最大值误连。

易错点总结

[!yellow]

  • 从每个数字都向后扩展,会在长连续序列上退化为 $O(n^2)$。
  • 重复值不能增加序列长度,应通过集合去重。
  • 排序后扫描虽能求解,但时间复杂度为 $O(n \log n)$,不满足题目要求。
  • 空数组应返回 0;答案从 0 初始化即可自然覆盖。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/54759261
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!