1. 为什么选择最长连续序列作为刷题突破口
这道题在力扣Hot100中排名靠前,编号128题,属于中等难度。表面上看是个简单的数组处理问题,但实际上考察了三个关键能力:数据结构的选择、边界条件的处理,以及时间复杂度的优化。这也是为什么很多面试官特别喜欢用它来考察候选人的基本功。
我第一次遇到这道题是在某大厂的二面,当时给出的初始解法用了排序+遍历,虽然通过了但被面试官追问"能否优化到O(n)"时直接卡壳。后来复盘发现,这正是区分普通程序员和优秀程序员的分水岭——能否在压力下快速识别问题本质。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目本质与暴力解法分析
给定一个未排序的整数数组nums,我们需要找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。例如:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]
2.1 最直观的排序解法
大多数人的第一反应是排序后遍历:
python复制def longestConsecutive(nums):
if not nums:
return 0
nums.sort()
max_len = current_len = 1
for i in range(1, len(nums)):
if nums[i] == nums[i-1] + 1:
current_len += 1
elif nums[i] != nums[i-1]: # 处理重复元素
max_len = max(max_len, current_len)
current_len = 1
return max(max_len, current_len)
时间复杂度O(nlogn),空间复杂度O(1)(如果允许修改原数组)。这个解法虽然简单,但已经能击败60%的提交,适合面试时快速给出保底方案。
2.2 暴力解法的致命缺陷
当面试官要求优化时,很多候选人会尝试用类似两数之和的哈希表记录法:
python复制def longestConsecutive(nums):
max_len = 0
num_set = set(nums)
for num in num_set:
current_num = num
current_len = 1
while current_num + 1 in num_set:
current_num += 1
current_len += 1
max_len = max(max_len, current_len)
return max_len
这个看似O(n)的解法实际是O(n²)——考虑[1,2,3,...,n]的情况,外层循环n次,内层while也是n次。我在面试时犯的正是这个错误。
3. 最优解法的思维突破点
3.1 避免重复计算的技巧
关键突破在于:只有当数字是序列的起始点时才开始计算。如何判断起始点?当前数字减一不在集合中时:
python复制def longestConsecutive(nums):
num_set = set(nums)
max_len = 0
for num in num_set:
if num - 1 not in num_set: # 是起始点
current_num = num
current_len = 1
while current_num + 1 in num_set:
current_num += 1
current_len += 1
max_len = max(max_len, current_len)
return max_len
这样每个数字最多被访问两次(作为序列的一部分和作为起始点检查),真正实现O(n)时间复杂度。
3.2 边界条件的魔鬼细节
实际编码时需要特别注意:
- 空数组返回0
- 数组中可能有重复数字(所以要先转set)
- 序列长度可能为1(所有数字都不连续)
- 负数也需要正确处理
4. 不同语言实现的性能差异
4.1 Python的优化版本
利用集合操作可以进一步精简:
python复制def longestConsecutive(nums):
nums = set(nums)
return max((len(list(g)) for k, g in groupby(n for n in nums if n-1 not in nums))), default=0)
但可读性会下降,面试时不推荐。
4.2 Java实现注意点
Java的HashSet.contains()是O(1)操作:
java复制class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> num_set = new HashSet<>();
for (int num : nums) {
num_set.add(num);
}
int max_len = 0;
for (int num : num_set) {
if (!num_set.contains(num - 1)) {
int current_num = num;
int current_len = 1;
while (num_set.contains(current_num + 1)) {
current_num += 1;
current_len += 1;
}
max_len = Math.max(max_len, current_len);
}
}
return max_len;
}
}
4.3 C++的特殊考量
unordered_set在C++中可能有更优的缓存性能:
cpp复制class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end());
int max_len = 0;
for (int num : s) {
if (!s.count(num - 1)) {
int current = num;
int len = 1;
while (s.count(current + 1)) {
current++;
len++;
}
max_len = max(max_len, len);
}
}
return max_len;
}
};
5. 实际面试中的变种问题
5.1 返回最长序列本身
如果面试官要求返回序列而不仅是长度,可以这样修改:
python复制def longestConsecutive(nums):
num_set = set(nums)
max_seq = []
for num in num_set:
if num - 1 not in num_set:
current_num = num
current_seq = [current_num]
while current_num + 1 in num_set:
current_num += 1
current_seq.append(current_num)
if len(current_seq) > len(max_seq):
max_seq = current_seq
return max_seq
5.2 流式数据场景
如果数据是流式输入的(无法一次性获取所有数字),可以考虑使用Union-Find数据结构,但实现复杂度会显著增加。
6. 刷题时的训练建议
- 先自己思考15分钟再看题解
- 写完后手动模拟运行边缘用例
- 记录每种解法的时间/空间复杂度
- 尝试用不同语言实现
- 每周复习做过的题目
我在刷Hot100时发现,连续刷同一类型的题目(如数组/哈希表类)效果更好。比如做完这道可以接着做:
- 41.缺失的第一个正数
- 217.存在重复元素
- 238.除自身以外数组的乘积
7. 算法思想的延伸应用
这种"用空间换时间"的思想在工程中很常见,比如:
- 用户ID去重检查
- IP地址范围快速查询
- 订单号连续性校验
理解了这个模式后,你会发现很多业务场景都能套用类似的优化思路。比如电商系统中检查优惠券编号是否连续,就可以直接复用这个算法。
