精选专栏链接 🔗
欢迎订阅,点赞+关注,每日精进1%,与百万开发者共攀技术珠峰
更多内容持续更新中~
【LeetCode 热题 100】最长连续序列
📝题目描述
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
bash
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
bash
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
示例 3:
bash
输入:nums = [1,0,1,2]
输出:3
💡提示信息
- 0 <= nums.length <= 10 5 10^5 105;
- -109 <= nums[i] <= 10 9 10^9 109;
方法一:排序 + 线性扫描
最直观的思路通常是先对数组进行排序。排序后,原本乱序的数字会变得有序,我们只需要一次遍历就能统计出最长的连续长度。
核心思路
- 判空处理:如果数组为空,直接返回 0;
- 数组排序:使用 Arrays.sort() 对数组进行升序排列、;
- 遍历统计:从第二个元素开始遍历,比较当前元素 nums[i] 与前一个元素 nums[i-1];
- 去重:如果两者相等,说明是
重复元素,直接跳过(continue); - 连续:如果 nums[i] == nums[i-1] + 1,说明数字连续,当前连续长度 currentLen 加 1;
- 断开:如果不满足上述条件,说明连续性被打断,将 currentLen 重置为 1;
- 更新最大值:在遍历过程中,时刻维护一个全局最大长度 maxLen;
- 去重:如果两者相等,说明是
Java 代码实现如下:
java
class Solution {
public int longestConsecutive(int[] nums) {
if(nums == null || nums.length == 0){
return 0;
}
// 1. 排序
Arrays.sort(nums);
int maxLen = 1; // 记录最长连续序列长度
int currentLen = 1; // 记录当前连续序列长度
// 2. 线性扫描
for(int i=1;i<nums.length;i++){
if(nums[i] == nums[i-1]){
continue; // 遇到重复元素,跳过
}else if(nums[i] == nums[i-1]+1){
currentLen++; // 连续,当前长度+1
}else{
currentLen = 1; // 不连续,重置当前长度为1
}
// 更新最大长度
maxLen = Math.max(maxLen,currentLen);
}
return maxLen;
}
}
提交代码,运行结果如下:

复杂度分析
- 时间复杂度:O(N log N)。主要开销在于数组的排序操作,后续的线性扫描仅为 O(N)。注意:这不符合题目要求的 O(N),但在实际面试中可以作为保底解法提出;
- 空间复杂度:O(1)(取决于排序算法的实现,若忽略排序所需的栈空间则为常数级)。
方法二:哈希集合 + 智能起点(最优解)
为了满足题目要求的 O(N) 时间复杂度,我们需要借助哈希表(HashSet)来实现 O(1) 的快速查找,并采用"智能起点"的策略来避免重复计算。
核心思路
- 去重与预处理:首先将所有数字存入
HashSet中。这一步既去除了重复元素,又为后续的 O(1)查找做好了准备; - 寻找序列起点:遍历集合中的每一个数字 num,我们需要判断它是否是一个连续序列的起点 。判断标准:检查集合中是否存在 num - 1;
- 如果 num - 1 存在,说明 num 不是起点(它前面还有数),直接跳过;
- 如果 num - 1 不存在,说明 num 是一个新序列的开头;
- 扩展序列:一旦找到起点 num,通过 while 循环不断检查 num + 1、num + 2... 是否存在于集合中,从而计算出该序列的长度;
- 更新最大值:计算出的序列长度与 maxLen 比较并更新。
Java 代码实现如下:
java
class Solution {
public int longestConsecutive(int[] nums) {
if(nums == null || nums.length == 0){
return 0;
}
// 1. 将所有元素存入 HashSet,实现去重和 O(1) 查找
HashSet<Integer> hashSet = new HashSet<>();
for(int num:nums){
hashSet.add(num);
}
int maxLen = 1;
// 2. 遍历集合中的每个数字
for(int num : hashSet){
// 3. 核心优化:只有当 num-1 不存在时,num 才是序列的起点
if(!hashSet.contains(num-1)){
int currentLen = 1;
int currentNum = num;
// 4. 从起点开始,不断向后查找连续的数字
while(hashSet.contains(currentNum+1)){
currentLen++;
currentNum++;
}
// 5. 更新最长序列长度
maxLen = Math.max(maxLen, currentLen);
}
}
return maxLen;
}
}
提交代码,运行结果如下:

复杂度分析
- 时间复杂度为O(N) 。其中 N 是数组 nums 的长度。虽然代码中有两层循环(外层 for 和内层 while),但实际上每个数字最多只会被访问 2 次;
- 空间复杂度为O(N) 。算法的核心是使用了 HashSet 来存储数组中的所有元素。
在最坏情况下(数组中没有重复元素),哈希表需要存储 N 个元素。其他变量只占用常数级别的额外空间;
总结
注意
虽然哈希表法在理论上拥有线性的 O ( N ) O(N) O(N) 时间复杂度,优于排序法的 O ( N log N ) O(N \log N) O(NlogN),但在实际运行(特别是数据规模 N N N 较小时)往往更慢。因为在 LeetCode 常规的数据规模下,哈希表法虽然在理论时间复杂度上占优,但单次操作涉及复杂的哈希计算与离散的内存随机访问,开销较大;而 Java 高度优化的底层排序算法凭借其极低的常数开销和 CPU 缓存连续访问优势,足以抵消哈希表法在理论复杂度上的微弱领先,从而在实际运行中表现得更快。
对比
| 对比维度 | 排序 + 线性扫描 | 哈希集合 + 智能起点 |
|---|---|---|
| 时间复杂度 | O ( N log N ) O(N \log N) O(NlogN) | O ( N ) O(N) O(N) |
| 空间复杂度 | O ( 1 ) O(1) O(1) | O ( N ) O(N) O(N) (需存储所有元素) |
| 实际执行效率 | 通常更快 | 相对较慢 |
| 适用场景 | 数据量较小、内存受限或对常数级性能敏感的场景 | 数据量极大、严格追求线性时间复杂度的场景 |
| 面试建议 | 作为基础解法提出,展示直观思路 | 作为最优解提出,重点解释"智能起点"去重逻辑 |
这道题考察的是如何利用哈希集合(HashSet)的特性,通过"只从序列起点开始向后延伸"的巧妙剪枝策略,在 𝑂(𝑁)线性时间内找出最长连续序列。