【Hot100-Java中等】/LeetCode 128. 最长连续序列:如何打破排序思维,实现 O(N) 复杂度?

在 LeetCode 的算法题中,"最长连续序列" (Longest Consecutive Sequence) 是一道非常经典的 Hot 100 题目。它考察的不是复杂的算法模板,而是对哈希表特性的灵活运用以及对时间复杂度的精确控制。

1. 题目核心难点

题目描述 :给定一个未排序的整数数组 nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

关键限制 :请你设计并实现时间复杂度为 的算法解决此问题。

为什么不能排序?

看到"连续序列",直觉反应通常是先排序,然后遍历一遍统计。

  • 排序算法 (如快速排序、归并排序)的时间复杂度下限是 。

  • 题目严格要求 ,这意味着排序是被禁止的。我们必须在不排序的情况下,通过"查找"来还原序列。


2. 核心思路:哈希表 + "跳过逻辑"

要在 时间内知道"某个数字是否存在",只能依靠 哈希表 (HashSet)。

步骤一:去重与快速查找

首先,将数组中所有元素放入 HashSet 中。

  • 目的 1 :实现 的查找复杂度。

  • 目的 2:去重(虽然这一步不是必须的,但遍历 Set 比遍历原数组更稳健,详见后文分析)。

步骤二:寻找序列的"起点" (关键优化)

这是算法能达到 的核心原因。

假设数组是 100, 4, 200, 1, 3, 2。哈希表中包含了这些数字。

当我们遍历到数字 3 时,我们要不要开始数数(寻找 4, 5...)?

  • 不要。 因为 3 的前面有 2。既然 2 存在,3 肯定不是一个连续序列的开头 。我们应该等待遍历到 2(甚至 1)时再处理。

  • 规则 :只有当 num - 1 不在哈希表中时,num 才是序列的起点,我们才开始向后计数。


3. 代码实现与详解

Java

复制代码
class Solution {
    public int longestConsecutive(int[] nums) {
        // 1. 使用 HashSet 存储所有数字,实现 O(1) 查询并去重
        Set<Integer> num_set = new HashSet<Integer>();
        for (int num : nums) {
            num_set.add(num);
        }

        int longestStreak = 0;

        // 2. 遍历哈希表中的每个数字
        for (int num : num_set) {
            // 3. 【关键判断】只有当 num 是序列的起点时(即 num-1 不存在),才开始匹配
            if (!num_set.contains(num - 1)) {
                int currentNum = num;
                int currentStreak = 1;

                // 4. 不断查询 num+1, num+2... 是否存在
                while (num_set.contains(currentNum + 1)) {
                    currentNum += 1;
                    currentStreak += 1;
                }

                // 5. 更新最大长度
                longestStreak = Math.max(longestStreak, currentStreak);
            }
        }

        return longestStreak;
    }
}

4. 深度解析:为什么这是 O(N)?

很多初学者看到 for 循环里套了一个 while 循环,第一反应就是:"这不是 吗?"

其实不然。我们要从每个元素被访问的次数来分析:

  1. 外层循环:每个元素最多被访问 1 次。

  2. 内层 while 循环:

    • 只有当一个数是"起点"时,才会进入 while。

    • 举例 [1, 2, 3, 4]。

      • 访问 4:4-1 存在,跳过。

      • 访问 3:3-1 存在,跳过。

      • 访问 2:2-1 存在,跳过。

      • 访问 1:1-1 不存在,是起点 。进入 while,依次访问 2, 3, 4。

    • 结论 :数组中的每个数字,只会被 while 循环内部访问最多一次(就是在统计属于它的那个序列长度时)。

总操作次数 (存入Set) + (外层遍历) + (内层while累计总次数)。

即 ,忽略常数后为 。


5. 易错点分析:遍历 nums 还是遍历 Set?

在实现时,有人会习惯写成 for (int num : nums) 来遍历原数组。虽然在大多数情况下也能通过,但存在隐患。

潜在风险:重复元素的陷阱

如果输入数组包含大量重复的"起点",例如:

nums = 1, 1, 1, ..., 1, 2, 3, 4, ... 1000

  • 遍历 nums :程序会遇到第一个 1,发现 0 不在,扫描一遍 1 到 1000。接着遇到第二个 1,又扫描一遍 1 到 1000......这将导致算法退化为 并超时。

  • 遍历 Set (官方解法):Set 天然去重。无论原数组有多少个 1,Set 中只有一个 1,内层循环只会执行一次。

因此,始终推荐遍历 Set 而不是原数组,这是保证算法稳定 的最佳实践。

总结

解决 LeetCode 128 题的关键在于转换思维:

  1. 空间换时间 :用哈希表代替排序,消除 的瓶颈。

  2. 剪枝优化 :通过 !set.contains(x-1) 这一判断,精准定位序列起点,避免了对同一个序列中间元素的重复无效计算,将复杂度严格控制在 。

相关推荐
UIU11411 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
论文复现现场11 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi11 小时前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构
秋天的怀念_David12 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
碧海银沙音频科技研究院13 小时前
在PC电脑上用Whisper→Marian算法实现离线语音翻译功能
算法·whisper
niyongsheng13 小时前
3D 装箱算法工程化:数据结构、放置启发式与支撑规则
数据结构·算法·rust
叉烧滑蛋15 小时前
B站台湾大学郭彦甫|MATLAB 学习笔记|04 变量与档案存取
数据结构
j7~16 小时前
【穷举、深搜与回溯算法】一棵决策树讲透全排列与子集
数据结构·c++·算法·leetcode·回溯·递归
傅科摆 _ py17 小时前
DP,贪心,堆和栈,二分力扣复习
开发语言·python·算法
Shaoshing17 小时前
LeetCode100 42.接雨水
java·算法·leetcode