「这是我参与2022首次更文挑战的第38天,活动详情查看:2022首次更文挑战」。
题目:给定一个没有排序的数组nums,要求找出数字连续的最长子序列的长度。要求实现的时间复杂度为。
例如输入的nums为[100, 4, 200, 1, 3, 2],其最长连续子序列为[1, 2, 3, 4],因此长度为4。
解题思路
暴力解法
最开始想到的还是先将数组进行排序,之后遍历数组,比较数组的前后两个值的大小,如果两个值相等或者前一个值等于后一个值加一,则判定连续,然后去除重复的,连续子序列加一,如果断了则重置初始子序列长度。代码可以参考下面的:
class Solution {
public int longestConsecutive(int[] nums) {
Arrays.sort(nums);//将数组进行排序
if(nums.length==0) return 0;//如果数组里面没有值,就返回0
int ans=1;//存储最终的结果
int temp=1;//存储中间的结果
for(int i=1;i<nums.length;i++){//遍历数组中的每一个值
if(nums[i]==nums[i-1]+1||nums[i]==nums[i-1]){//如果前一个值等于后一个值+1,或者两个相等,就判断成是连续的
if(nums[i]!=nums[i-1]) temp++;//如果不相等,连续子序列长度就+1,否则不加
ans=Math.max(ans,temp);//判断是否大于之前遇到的最长连续子序列
}else{
temp=1;//如果连续断了,就重置temp
}
}
return ans;//返回最终的结果
}
}
但该方式的时间复杂度不满足题目要求的,因此考虑换一种思路。
哈希表存储元素
题目中所给的数组有时可能会包含大量重复元素,此时考虑将数组中元素存入哈希表,这样就能避免上述方法的相等字符需要跳跃的问题。并且遍历数组的每一个元素,可以利用哈希表来判断当前元素是否属于子序列的边界,如果是则执行下一次循环,开始下一个元素的判断。如果不是则此时的元素即为一个边界,在哈希表中查找当前元素不断自增后的元素是否在哈希表内,如果存在则子序列长度也自增,最后更新最长子序列长度即可,可得代码如下:
public int longestConsecutive(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for(int value:nums){
set.add(value);
}
int longCon = 0;
for(int element:nums){
if(set.contains(element-1)){
continue;
}else {
int len=0;
while(set.contains(element++)){
len++;
}
longCon = Math.max(longCon, len);
}
}
return longCon;
}
上述代码时间复杂度为,空间复杂度也为。但代码最终耗时337ms,导致耗时如此长的原因是在上面代码中遍历的是nums数组,因为示例数组可能包含大量重复元素,因此我们只需要遍历set集合即可,即将第二个for循环改为:
for(int element:set){
if(set.contains(element-1)){
continue;
...
}
}
此时仅仅耗时12ms。