128. 最长连续序列
✨核心逻辑
本题要求在 时间复杂度 O(N) 的约束下解决问题,因此不能使用常规的排序方法(排序最低也是 O(N log N))。这里采用 哈希集合(HashSet)优化查找 的策略:
- 去重并快速查找:将数组中的所有数字放入一个
HashSet中。这样可以在O(1)的时间内判断某个数字是否存在。 - 找连续序列的起点:遍历集合中的每一个数字。如果当前数字
integer - 1存在于集合中,说明integer不是一段连续序列的起点(它是序列中间或末尾的元素),我们直接跳过它,不做处理。 - 从起点向后探测:只有当
integer - 1不存在时,说明integer是一段连续序列的起点。此时我们利用while循环不断向后寻找num + 1,统计出这段连续序列的长度。 - 更新最大长度:每次统计完一段连续序列的长度后,与当前记录的全局最大长度
max1进行比较并更新。
这种方法确保了大循环中的 while 探测,在整个算法执行过程中,集合中的每个数字最多只会被访问一次,总体时间复杂度被降低到了 O(N)。
🔥代码实现(含详细变量注释)
class Solution {
public int longestConsecutive(int[] nums) {
// set:哈希集合,用于存储数组中的所有元素,去除重复值,同时提供 O(1) 的查找性能
Set<Integer> set = new HashSet<>();
for (int i : nums) {
set.add(i);
}
// max1:记录当前发现的最长连续序列的长度,初始值为 0
int max1 = 0;
// 遍历哈希集合中的每一个数字
for (Integer integer : set) {
// 核心优化:判断当前数字 integer 是否为一段连续序列的起点
// 如果集合中存在 integer - 1,说明当前数字不是序列的起点,直接跳过,避免重复计算
if (!set.contains(integer - 1)) {
// num:从当前起点开始,用于不断向后寻找连续的下一个数字
int num = integer;
// max2:记录当前探测到的这段连续序列的长度,初始为 1(包含自身)
int max2 = 1;
// 只要集合中存在 num + 1,说明连续关系还在继续
while (set.contains(num + 1)) {
num = num + 1; // 往后推一位
max2++; // 当前连续序列长度 +1
}
// 更新全局最大连续序列的长度
max1 = Math.max(max1, max2);
}
}
// 返回找到的最长连续序列的长度
return max1;
}
}
- ⏱️复杂度分析
时间复杂度:O(N),其中 N 是数组的长度。虽然代码中有双层嵌套循环结构(外层 for 套内层 while),但由于我们只从序列的起点开始遍历,内层 while 循环在整个程序的执行过程中,集合中的每个元素最多只会被访问一次。因此总操作次数与数组元素总数成正比。
空间复杂度:O(N),额外使用了一个哈希集合 set 来存储数组中的所有元素,所需空间与输入数组的大小线性相关。
🔍总结一下:
int的范围是 -2.1的10^9 到 2.1的10^9,需要记一下,有些题会给10的多少次方的范围
疑问点:时间复杂度是O n方,举个例子,直观的123456
真正的 N方 是指每个元素走到时,都要重复去遍历所有元素;但这道题的连续序列 [1,2,3,4,5,6] 中,只有起点 1 触发了 while 并一次性地扫完了 2,3,4,5,6。当外层循环走到 2,3,4,5,6 时,它们遇到前面的 if 判断直接就被‘判出局’了,连内层 while 的大门都没进去。真正走完的直有最开始的 1