题目
给定一个未排序的整数数组,找出最长连续序列的长度。要求算法的时间复杂度为 O(n)。链接
思路
暴力方法很好想,set去重,然后遍历每个元素,查找他后面的元素
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer>set=new HashSet<>();
for(int x:nums){
set.add(x);
}
int res=0;
for(int x:set){
int next=x;
int tempLen=0;
while(set.contains(next)){
next=next+1;
tempLen++;
}
res=Math.max(res,tempLen);
}
return res;
}
}
但这样时间复杂度太高了
- 我们的算法时间复杂度最坏情况下还是会达到 O ( n 2 ) O(n^2) O(n2)(即外层需要枚举 O ( n ) O(n) O(n) 个数,内层需要暴力匹配 O ( n ) O(n) O(n) 次),无法满足题目的要求。但仔细分析这个过程,我们会发现其中执行了很多不必要的枚举,如果已知有一个 x , x + 1 , x + 2 , ⋯ , x + y x, x+1, x+2,⋯,x+y x,x+1,x+2,⋯,x+y 的连续序列,而我们却重新从 x + 1 , x + 2 x+1,x+2 x+1,x+2 或者是 x + y x+y x+y 处开始尝试匹配,那么得到的结果肯定不会优于枚举 x x x 为起点的答案,因此我们在外层循环的时候碰到这种情况跳过即可。
- 那么怎么判断是否跳过呢?由于我们要枚举的数 x x x 一定是在数组中不存在前驱数 x − 1 x-1 x−1 的,不然按照上面的分析我们会从 x − 1 x-1 x−1开始尝试匹配,因此我们每次在哈希表中检查是否存在 x − 1 x-1 x−1 即能判断是否需要跳过了。
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer>set=new HashSet<>();
for(int x:nums){
set.add(x);
}
int res=0;
for(int x:set){
//代码中只需要加这一行判断即可
if(!set.contains(x-1)){
int next=x;
int tempLen=0;
while(set.contains(next)){
next=next+1;
tempLen++;
}
res=Math.max(res,tempLen);
}
}
return res;
}
}