从 LeetCode #128 学算法与 Python:哈希表、O(n) 思维与函数/方法的本质区别
关键词 :最长连续序列|哈希表|时间复杂度 O(n)|
set|函数调用 vs 方法调用|Python 基础
一、引子:一道题,两个层次的收获
最近在刷 LeetCode 第 128 题 「最长连续序列」 时,我不仅学会了一种巧妙的 O(n) 解法,还意外暴露了自己对 Python 基础语法的一些模糊认知------比如:
- 为什么
set(nums)正确,而nums.set()会报错? len()是函数还是方法?为什么不能写成arr.len()?- 两层循环怎么可能是 O(n)?
这些问题看似琐碎,实则关乎编程底层逻辑的理解。于是,我决定把这道题的解法和背后的 Python 原理一起梳理清楚。
二、题目回顾:最长连续序列(LeetCode #128)
题目 :给定一个未排序的整数数组 nums,找出数字连续的最长序列的长度。
要求 :时间复杂度为 O(n)
示例:
python
输入: [100, 4, 200, 1, 3, 2]
输出: 4 # 因为 [1,2,3,4] 是最长连续序列
三、核心解法:哈希表 + 起点判断(O(n))
✅ 为什么不能排序?
- 排序最低时间复杂度是 O(n log n),不满足题目要求。
- 必须用 空间换时间 → 引入哈希表(Python 中用
set实现)。
✅ 我的四步解题法
步骤 1:用 set(nums) 去重并支持 O(1) 查找
python
num_set = set(nums)
- 自动去除重复数字(重复值不影响连续长度)
- 后续可用
x in num_set在 O(1) 时间判断是否存在
💡 注意:这里必须写
set(nums),不是nums.set()(后面详解原因)
步骤 2:只从"连续序列的起点"开始扩展
- 起点定义 :
num是起点 ⇨num - 1不在集合中 - 这样可避免重复计算(如从 2 开始数 [2,3,4],不如从 1 开始数 [1,2,3,4])
步骤 3:用 while 向右扩展
python
while current_num + 1 in num_set:
current_num += 1
current_length += 1
- 只要下一个数存在,就继续延伸
步骤 4:更新全局最大长度
python
max_length = max(max_length, current_length)
✅ 完整代码
python
from typing import List
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
num_set = set(nums)
max_length = 0
for num in num_set:
if num - 1 not in num_set: # 判断是否为起点
current_num = num
current_length = 1
while current_num + 1 in num_set: # 向右扩展
current_num += 1
current_length += 1
max_length = max(max_length, current_length)
return max_length
四、为什么时间复杂度是 O(n)?
虽然有 for + while 两层循环,但:
- 每个数字最多被访问两次 :
- 外层循环判断是否为起点
- 内层循环作为连续序列的一部分被扩展
- 总操作次数 ≤ 2n → 时间复杂度 O(n)
🌟 这是"看似嵌套循环,实则线性时间"的经典案例!
五、关键 Python 基础:函数调用 vs 方法调用
在写这道题时,我一度困惑:为什么是 set(nums) 而不是 nums.set()?这引出了一个根本问题:
🔍 函数(Function) vs 方法(Method)
| 类型 | 调用形式 | 特点 | 示例 |
|---|---|---|---|
| 函数 | function_name(参数) |
独立工具,通用操作 | len(arr), sorted(s), set(nums) |
| 方法 | 对象.method() |
对象自带的功能 | arr.append(x), "abc".upper(), s.add(x) |
✅ 常见函数(你刷题必用)
python
len(obj) # 获取长度 → len([1,2]) → 2
sorted(iterable) # 返回新排序列表
set(iterable) # 转为集合(去重 + O(1) 查找)
list(iterable) # 转为列表
range(n) # 生成数字序列
enumerate(arr) # 枚举 (index, value)
✅ 常见方法(按数据结构分类)
python
# list
arr.append(x), arr.pop(), arr.sort()
# str
s.upper(), s.split(), s.replace()
# set
s.add(x), s.remove(x), s.union(other)
# dict
d.get(key), d.keys(), d.items()
❌ 常见错误(千万别写!)
python
[1,2].len() # ❌ 应写 len([1,2])
"bac".sorted() # ❌ 应写 sorted("bac")
[1,2,2].set() # ❌ 应写 set([1,2,2])
💡 口诀 :
通用操作用函数(len,set,sorted),对象专属用方法(.append,.upper)
六、为什么 set(nums) 是函数,不是方法?
-
set是 Python 的内置类型 -
set(nums)是调用其构造函数,将可迭代对象转为集合 -
而
list类型没有.set()方法 ,所以nums.set()会报错:pythonAttributeError: 'list' object has no attribute 'set'
类似地:
list("ab")✅,但"ab".list()❌str(123)✅,但123.str()❌
七、本题带给我的双重成长
| 算法层面 | Python 语言层面 |
|---|---|
| ✅ 学会"只从起点扩展"的 O(n) 思想 | ✅ 分清函数 vs 方法 |
| ✅ 理解哈希表在去重与快速查找中的作用 | ✅ 掌握 set(nums) 的正确用法 |
| ✅ 能分析"嵌套循环但线性时间"的复杂度 | ✅ 避免 AttributeError 类低级错误 |
八、总结:刷题不仅是练算法,更是练语言
这道「最长连续序列」让我明白:
优秀的解法 = 清晰的算法思维 + 扎实的语言基础
- 如果不懂
set的 O(1) 查找特性,就想不到 O(n) 解法; - 如果混淆函数与方法,就会写出语法错误,浪费调试时间。
因此,刷题时不要只盯着"通过",更要问自己:
- 这个数据结构为什么适合这个问题?
- 这个函数/方法的底层原理是什么?
- 我能否把这种模式迁移到其他题目?