1. 问题背景与理解
这道题目在LeetCode上编号为128,题目名为"最长连续序列"。给定一个未排序的整数数组nums,要求找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
举个例子:
- 输入:[100,4,200,1,3,2]
- 输出:4
- 解释:最长连续序列是[1,2,3,4],长度为4
这个问题看似简单,但有几个关键点需要注意:
- 序列中的数字必须是连续的,但不要求它们在原数组中是相邻的
- 序列可以递增也可以递减,但题目通常默认要求递增序列
- 数组中可能有重复元素,这些重复元素不应该影响序列长度计算
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 暴力解法分析
2.1 直观思路
最直观的解法是对每个数字,检查它是否能作为某个连续序列的起点,然后向后查找连续的数字。具体步骤:
- 对于数组中的每个数字num
- 检查num-1是否存在于数组中,如果不存在,则num可能是一个序列的起点
- 如果num是起点,则不断检查num+1,num+2...是否在数组中
- 记录能找到的最长序列长度
2.2 代码实现
python复制def longestConsecutive(nums):
if not nums:
return 0
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
2.3 复杂度分析
时间复杂度:O(n^2) - 最坏情况下,对于每个元素都要遍历整个数组
空间复杂度:O(n) - 使用了哈希集合存储元素
虽然这种方法简单直观,但对于大规模数据效率太低,我们需要更优的解法。
3. 哈希集合优化解法
3.1 优化思路
观察到暴力解法中有很多重复检查,我们可以通过以下优化提高效率:
- 先将所有数字存入哈希集合,实现O(1)时间查询
- 只有当数字是一个序列的起点时才进行向后查找
- 使用哈希集合避免重复处理同一序列
3.2 优化后代码
python复制def longestConsecutive(nums):
num_set = set(nums)
max_length = 0
for num in num_set:
# 只有当num是序列起点时才处理
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
3.3 复杂度分析
时间复杂度:O(n) - 每个数字最多被访问两次(一次在外部循环,一次在内部while循环)
空间复杂度:O(n) - 存储哈希集合
这个解法已经相当高效,是面试中最常被接受的答案。
4. 并查集解法
4.1 并查集简介
并查集(Disjoint Set Union)是一种数据结构,擅长处理不相交集合的合并与查询问题。它支持两种操作:
- Find:查找元素所属集合
- Union:合并两个集合
4.2 应用于本题的思路
我们可以将连续的数字视为应该属于同一集合的元素:
- 初始化时,每个数字是自己的父节点
- 对于每个数字,如果num+1存在,则将它们合并
- 最后统计最大的集合大小
4.3 并查集实现
python复制class DSU:
def __init__(self, nums):
self.parent = {num: num for num in nums}
self.size = {num: 1 for num in nums}
def find(self, x):
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]] # 路径压缩
x = self.parent[x]
return x
def union(self, x, y):
x_root = self.find(x)
y_root = self.find(y)
if x_root == y_root:
return
# 按秩合并
if self.size[x_root] < self.size[y_root]:
self.parent[x_root] = y_root
self.size[y_root] += self.size[x_root]
else:
self.parent[y_root] = x_root
self.size[x_root] += self.size[y_root]
def longestConsecutive(nums):
if not nums:
return 0
nums = list(set(nums)) # 去重
dsu = DSU(nums)
num_set = set(nums)
for num in nums:
if num + 1 in num_set:
dsu.union(num, num + 1)
return max(dsu.size.values())
4.4 复杂度分析
时间复杂度:O(nα(n)) - 其中α是反阿克曼函数,可以认为是常数时间
空间复杂度:O(n) - 存储父指针和集合大小
虽然理论复杂度很好,但实际应用中常数因子较大,通常不如哈希集合解法高效。
5. 排序解法
5.1 排序思路
另一种直观解法是先排序,然后遍历寻找最长连续序列:
- 对数组进行排序
- 遍历排序后的数组,统计连续序列长度
- 处理重复元素和数组边界情况
5.2 代码实现
python复制def longestConsecutive(nums):
if not nums:
return 0
nums = sorted(list(set(nums))) # 去重后排序
max_length = current_length = 1
for i in range(1, len(nums)):
if nums[i] == nums[i-1] + 1:
current_length += 1
max_length = max(max_length, current_length)
else:
current_length = 1
return max_length
5.3 复杂度分析
时间复杂度:O(nlogn) - 主要由排序决定
空间复杂度:O(1) - 如果不考虑排序所需空间
虽然不如线性解法高效,但在某些情况下实现简单,适合快速编写。
6. 边界条件与测试用例
6.1 常见边界情况
- 空数组输入:应返回0
- 所有元素相同:应返回1
- 数组中存在重复元素:应正确处理
- 数组中有负数:应正确处理
- 最大连续序列在数组末尾或开头
6.2 测试用例示例
python复制test_cases = [
([100,4,200,1,3,2], 4),
([0,3,7,2,5,8,4,6,0,1], 9),
([], 0),
([1], 1),
([1,1,1,1], 1),
([0,-1], 2),
([1,3,5,7,9], 1)
]
for nums, expected in test_cases:
assert longestConsecutive(nums) == expected
7. 实际应用与变种问题
7.1 实际应用场景
- 数据库中的连续ID查询优化
- 时间序列数据分析
- 游戏中的连续成就系统
- 社交网络中的连续活跃天数统计
7.2 常见变种问题
- 允许序列中有固定大小的间隔(如允许跳过最多k个数字)
- 二维矩阵中的最长连续序列
- 在流数据中实时计算最长连续序列
- 同时考虑递增和递减序列
8. 性能比较与选择建议
8.1 各解法性能对比
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力解法 | O(n^2) | O(1) | 不推荐使用 |
| 哈希集合 | O(n) | O(n) | 大多数情况首选 |
| 并查集 | O(nα(n)) | O(n) | 需要频繁合并集合时 |
| 排序 | O(nlogn) | O(1)或O(n) | 数据量小或已排序 |
8.2 选择建议
- 面试中最常考察哈希集合解法,建议优先掌握
- 实际工程中,如果数据规模不大,排序解法可能更易维护
- 并查集解法在相关变种问题中可能更有优势
- 暴力解法仅用于理解问题本质,不应在实际中使用
9. 常见错误与调试技巧
9.1 常见实现错误
- 忘记处理空输入情况
- 没有正确处理重复元素
- 序列长度计算错误(如初始值设为0而不是1)
- 没有使用哈希集合导致超时
- 边界条件处理不当(如最小/最大整数)
9.2 调试技巧
- 使用小规模测试数据手动验证
- 打印中间变量检查逻辑是否正确
- 特别注意循环的起始和终止条件
- 对于复杂解法,先编写辅助函数并单独测试
- 使用assert语句验证关键假设
10. 扩展学习与相关题目
10.1 推荐练习题目
- LeetCode 674. 最长连续递增序列
- LeetCode 485. 最大连续1的个数
- LeetCode 300. 最长递增子序列
- LeetCode 1027. 最长等差数列
10.2 进阶学习资源
- 《算法导论》中关于哈希表和并查集的章节
- 算法可视化网站观察不同解法的执行过程
- 参加LeetCode周赛体验实际竞赛环境
- 阅读LeetCode官方题解中的多种解法比较
