LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂

刷到这道题的时候,我第一反应:这不排个序就完事了?遍历一遍数连续数字的长度,记录最大值,多简单啊。结果定睛一看题目要求时间复杂度 O (n),瞬间人傻了 ------ 排序本身都 O (nlogn) 了,这不是明摆着不让用排序嘛。

今天就聊聊这道经典题,从排序的朴素解法,到真正满足要求的 O (n) 解法,我把我踩过的坑都给你们捋一遍。

先说说题目到底要干啥

一句话说清题意:给你一个未排序的整数数组,找出数字连续的最长序列的长度。序列元素不用在原数组里挨在一起,只要数字本身是连续的就行。比如输入 [100,4,200,1,3,2],最长连续序列是 1、2、3、4,输出 4。

我的第一版:排序大法(能过但不对)

说实话,我第一反应写的就是排序。思路特别直白:先把数组从小到大排好序,然后从头往后遍历。如果当前数比前一个大 1,就说明连续,长度 +1;不然就重置长度,全程记录最大的长度值。

这个写法跑示例全对,提交也能 AC,但严格来说不符合题目 O (n) 的要求。面试的时候你这么写,面试官大概率会追问一句 "有没有时间复杂度更低的写法?"属于 "保底可以,但冲不了高分" 的解法。

正解来了:哈希集合 + 起点判断

想了好一会儿我才反应过来:要 O (1) 判断一个数字存不存在,那肯定用哈希集合啊。但光有集合还不够 ------ 总不能每个数字都往后挨个查有没有连续数吧,那不又退化成 O (n²) 了。

这里最关键的一个思路点,我当初琢磨了半天才想通:我们只从「连续序列的起点」开始计数。

什么叫起点?如果一个数字 num,它的前一个数 num - 1 不在集合里,那它一定是某段连续序列的开头。反过来,如果 num - 1 存在,那 num 肯定是某段序列中间的一个数,不用管它,等遍历到起点的时候自然会数到它。

就拿示例来说:集合里有 1、2、3、4、100、200。1 的前面 0 不存在,所以 1 是起点;2 的前面 1 存在,所以 2 不是起点,直接跳过。

这样一来,每个数字最多只会被访问一次。只有起点会进入内层循环往后数,中间的数都直接跳过,总的操作次数加起来就是 n 次,时间复杂度自然就是 O (n)。

我一开始还纳闷,这不也是嵌套循环吗?怎么就 O (n) 了?后来自己手算了一遍才明白:不是每个元素都会进内层循环,进循环的只有每个序列的第一个数,整体摊下来还是线性的。

代码实现

直接上 JavaScript 版本,亲测能提交通过:

javascript 复制代码
var longestConsecutive = function(nums) {
    // 空数组直接返回0,别问,问就是在这里错过一次
    if (nums.length === 0) return 0

    // 用Set去重 + 实现O(1)查找
    const numSet = new Set(nums)
    let maxLen = 0

    for (const num of numSet) {
        // 只有前一个数不存在时,才是序列的起点
        if (!numSet.has(num - 1)) {
            let currentNum = num
            let currentLen = 1

            // 从起点开始,往后数连续的数字
            while (numSet.has(currentNum + 1)) {
                currentNum++
                currentLen++
            }

            // 更新最大长度
            maxLen = Math.max(maxLen, currentLen)
        }
    }

    return maxLen
};

说个小细节:这里遍历的是 Set 而不是原数组。这样天然就避开了重复元素的问题,比如 [1,0,1,2] 这种有重复的情况,不会因为两个 1 重复计算。

复杂度分析

时间复杂度 O (n):每个元素最多被访问一次,整体线性时间。空间复杂度 O (n):需要一个哈希集合存储所有元素。

聊聊我踩过的坑

  1. 上来就写排序,写完才想起题目要 O (n),白忙活了好一会儿。
  2. 最开始写哈希版的时候,每个数都进循环往后数,直接超时,完全没达到 O (n) 的效果。
  3. 忘了处理空数组的边界情况,提交直接报错,社死现场。

最后碎碎念

其实这题本身逻辑不难,难的是跳出 "排序" 的惯性思维。那个 "只从起点开始遍历" 的思路,我觉得是这题最精髓的地方 ------ 一下子就把嵌套循环的时间复杂度拉回了线性。

你第一次做这道题的时候,第一反应是排序吗?或者你有别的更有意思的解法?评论区聊聊,我每条都会看~

觉得有帮助的话,点个赞让更多小伙伴看到呀

相关推荐
稚南城才子,乌衣巷风流8 分钟前
ST 表(Sparse Table)算法详解:原理、实现与应用
算法
hold?fish:palm14 分钟前
9 找到字符串中所有字母异位词
c++·算法·leetcode
Sw1zzle41 分钟前
算法入门(六):贪心算法 - 基础入门(Leetcode 121/455/860/376/738)
算法·leetcode·贪心算法
青山木1 小时前
Hot 100 --- 岛屿数量
java·数据结构·算法·leetcode·深度优先·广度优先
软件开发技术深度爱好者1 小时前
小学英语单词魔法学园HTML5实现
javascript·html5·英语学习
不会就选b1 小时前
算法日常・每日刷题--<归并排序>1
数据结构·算法
勉灬之1 小时前
Next.js + Prisma 跨平台部署踩坑记
开发语言·javascript·ecmascript
糖果店的幽灵1 小时前
langgraph分支之 - 动态分支(Dynamic Branch)
java·前端·javascript·人工智能·langgraph
危桥带雨1 小时前
排序算法(快排、归并、计数、基数排序)
数据结构·算法·排序算法