一、开篇导入
很多初学Python算法的朋友,都会卡在最长连续序列这道经典题上。
它看着简单,就是找数组里最长的连续数字片段,但藏着很多容易踩的坑:比如为什么不能用列表查找、为什么不用排序、代码每一行到底为什么这么写、下一行代码的逻辑衔接是什么。
我之前初学的时候,也是死记硬背代码,换个测试用例就不会做,完全不懂底层逻辑。后来慢慢梳理清楚每一步的衔接逻辑,才彻底吃透这道题。
今天讲明白为什么这么写,不那么写,零基础也能轻松看懂。
二、题目简介
题目:最长连续序列
给定一个未排序的整数数组 nums,找出数字连续的最长序列的长度。
要求:时间复杂度 O(n),序列数字无需在原数组中相邻,只需数值连续即可。
示例:输入 nums = [100,4,200,1,3,2],最终最长连续序列为 [1,2,3,4],长度为 4。
三、带故事的解题思路
我们可以把这道题想象成找连续的数字小队。
数组里的所有数字,就是零散站着的队员,杂乱无章、还有重复的队员。我们的任务就是:找出人数最多、编号连续的小队,统计出这个小队的人数。
首先,重复的队员没有用,留着只会重复统计、浪费时间,所以第一步要去重;其次,我们需要快速判断某个数字(队员)是否存在,不能一个个挨个找,不然速度太慢。
最重要的一点:我们只需要从每个小队的第一个人开始统计。
比如小队是1、2、3、4,我们只需要从1开始数就行。如果从2、3、4开始数,都是重复干活,完全没必要。
那怎么判断谁是小队第一个人?很简单:如果一个数字的前一个数(x-1)不存在,那它就是新小队的起点。
找到起点后,我们就往后挨个找连续的数字,直到数字断开,最后统计出这个小队的人数,对比记录最大的人数,就是最终答案。
顺着这个生活化的思路,就有了我们的完整代码,下面逐行拆解每一段逻辑。
四、完整总代码块
先给大家放完整可运行的代码,后面逐行拆解,方便大家对照查看。
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
st = set(nums)
ans = 0
for x in st:
if x - 1 in st:
continue
y = x + 1
while y in st:
y += 1
ans = max(ans, y - x)
return ans
五、逐行代码详细拆解
所有代码单独封装黑块,每一行讲清:上一行做完什么,下一行为什么这么写,语法规则、作用、注意事项。
1. 定义解题类
class Solution:
逻辑讲解:这是LeetCode平台固定语法规则,所有算法题的解题代码,都必须写在Solution类当中,平台会自动识别调用。没有这一行,代码无法正常判题运行。
上下行衔接:无前置代码,是整段代码的开头,为后续定义解题函数做基础框架。
2. 定义核心解题函数
def longestConsecutive(self, nums: List[int]) -> int:
逻辑讲解:在刚刚定义的类里面,创建一个专门用来解题的方法。self是类方法必备参数,属于Python固定语法,无需手动传值;nums是接收的题目输入数组;末尾标注int,代表这个函数最终会返回一个整数结果。
上下行衔接:上一行搭建好了类框架,这一行就需要在框架内,定义真正实现解题功能的函数。
3. 数组转集合,去重+快速查询
st = set(nums)
逻辑讲解:赋值语法:把右侧 set(nums) 的结果,赋值给左侧变量 st。set的作用有两个,第一是给数组去重,删除重复数字,避免重复计算;第二是集合查询元素的时间复杂度是O(1),速度远快于列表,能满足题目O(n)的时间要求。变量st是set的简写,只是方便书写,可自定义。
上下行衔接:函数初始化完成,接下来第一步就是处理原始数据,优化数据结构,为后续遍历查询做准备。
4. 初始化结果变量
ans = 0
逻辑讲解:把数字0赋值给变量ans。ans是answer的简写,用来全程记录当前找到的最长连续序列长度。初始值设为0,是为了适配空数组的情况,没有数字时结果就是0。
上下行衔接:上一行已经处理好数据,这一行需要定义一个变量,用来存放后续计算的答案结果。
5. 遍历所有不重复数字
for x in st:
逻辑讲解:Python遍历语法,依次取出集合st里的每一个数字,赋值给x,逐个判断处理。之所以遍历集合不遍历原数组,是因为集合已经去重,避免重复处理相同数字,节省运行时间。
上下行衔接:数据、结果变量都准备完毕,接下来需要逐个检查每一个数字,判断它是否为连续序列的起点。
6. 判断当前数字是否不是序列起点
if x - 1 in st:
逻辑讲解:判断当前数字x的前一个数字x-1,是否存在于集合中。如果存在,说明x排在某个连续序列的中间或末尾,不是新序列的起点,不需要从这个数开始统计。
上下行衔接:上一行拿到了单个数字x,这一行就要对x做核心判断,筛选出有效的序列起点。
7. 跳过无效数字,进入下一次循环
continue
逻辑讲解:continue是循环专属语法,作用是直接跳过本次循环剩余的所有代码,立刻进入下一轮循环。既然当前x不是序列起点,就无需后续计算,直接跳过即可。
上下行衔接:上一行判断出x不是有效起点,这一行就执行跳过操作,避免无效运算。
8. 定义后续连续数字起始值
y = x + 1
逻辑讲解:赋值语法,将x的后一位数字赋值给y。此时已经确定x是序列起点,接下来需要从x的下一个数字开始,往后查找所有连续数字。
上下行衔接:前面筛选完毕,能执行到这一行的x,全部都是有效序列起点,这一步开始准备往后匹配连续数字。
9. 循环匹配所有连续数字
while y in st:
逻辑讲解:while循环语法,只要当前y存在于集合中,说明数字连续,就持续执行循环体代码,不断向后匹配。
上下行衔接:上一行确定了第一个要匹配的后续数字y,这一行通过循环,持续查找所有相连的连续数字。
10. 数字后移,继续查找
y += 1
逻辑讲解:等价于 y = y + 1,每次匹配到存在的连续数字,就让y向后移动一位,继续判断下一个数字是否连续,直到数字断开为止。
上下行衔接:上一行判断y存在、数字连续,这一行就更新y的值,继续往后查找更长的序列。
11. 更新最长序列长度
ans = max(ans, y - x)
逻辑讲解:赋值语法,右侧用max函数对比「之前记录的最长长度ans」和「当前序列的长度y-x」,取更大的数值,重新赋值给ans。y是第一个断开的数字,所以y-x刚好是当前连续序列的总长度。
上下行衔接:while循环结束,当前这一段连续序列已经查找完毕,这一行需要统计长度并更新最优答案。
12. 返回最终结果
return ans
逻辑讲解:函数结束语法,将最终记录的最长序列长度ans返回出去,交给判题系统识别结果。
上下行衔接:所有数字遍历、统计、对比全部完成,这一行输出最终答案,结束整个解题逻辑。
六、新手易错坑点总结
1、不要用列表代替集合:列表in查询速度慢,大数据量会超时,集合是这道题的最优数据结构。
2、不要去掉起点判断:如果不判断x-1是否存在,会重复统计每一段序列,导致代码效率极低。
3、不要用排序解法:排序时间复杂度是O(n log n),不满足题目O(n)的核心要求,只能做理解用,不能作为标准答案。
4、空数组无需额外处理:代码初始ans=0,空数组不会进入循环,直接返回0,刚好适配边界情况。
七、结尾互动
这道最长连续序列的核心逻辑,到这里就全部拆解完了,每一行代码的衔接、语法、作用、易错点都讲得很透彻,没有任何跳步。
如果这篇逐行详解的干货内容对你学算法、练代码有帮助,欢迎点赞+收藏,后续会持续更新零基础、不跳步的Python算法讲解,大家可以慢慢跟着学。