这里是题目描述:LeetCOde-最长连续序列
如果题干中不对时间复杂度做 O(n) 的限制的话,这道题没有什么难度,我们可以对数组排序后使用双指针(或者基于贪心算法)来解本题,时间复杂度为O(nlogn)。但是加上时间复杂度不超过 O(n) 这一限制后,我们不能再采用先排序再贪心的方法,因为大多数排序算法的时间复杂度都为O(nlogn),计数排序理论上的时间复杂度为O(n),但是这有限制条件——被排序的数组必须足够“紧凑”,如果被排序数组过于“稀疏”时间复杂度便不再是O(n)
我们借助哈希表来实现 O(n) 时间复杂度下对本题的解。将输入数组nums
的包含的所有数字存入哈希表中,这有两个目的:一是去除nums
中的重复数字,二是可以在 O(1) 时间开销下确定某个数字是否存在于数组nums
中。基于哈希表的初步方法:将nums
中的数字存入哈希表中;然后遍历数组nums
(也可以使用foreach遍历哈希表的键),然后确定以当前遍历到的数字x
为开头的连续序列长度,从x+1
开始判断x+1
是否存在于哈希表中,接下来判断x+2
、x+3
… 直到x+y
不存在于哈希表中,则此连续序列最大的数字是x+y-1
,则该子序列长度是y
,将所有的这些子序列中最大的长度作为最终结果返回。
但这种方法的时间复杂度仍然为O(n2),原因在于:我们确定了以x
为开头,以x+y-1
为结尾的长度为y
的连续序列x~x+y-1
后,但仍然会遍历到数字x+m (0<m<y)
,以它开头的连续序列为x+m~x+y-1
,实际上包含在x~x+y-1
中,且肯定不是最长连续序列,实际上我们对一个连续序列的子序列做了无用而重复的搜索。为了避免这种重复,需要保证对每一段连续序列只做一次搜索,我们采用下面两种方法来实现
基于哈希表的方法1
对于遍历到的一个数字x
,我们首先判断nums
中是否存在它的前驱数字x-1
,如果存在,证明x
不是一个连续序列的第一个数字,跳过x
;如果不存在x-1
,则x
是一个连续序列的第一个数字,不跳过,开始搜索x+1
、x+2
…是否存在来确定子序列长度。这样保证了每个连续子序列只被搜索一遍,时间复杂度为O(n)
基于哈希表方法1:
class Solution {
public int longestConsecutive(int[] nums) {
if(nums.length<=1)
{
return nums.length;
}
//存储nums中去重后的元素
HashSet<Integer> hashSet=new HashSet<>();
for(int i=0;i<nums.length;i++)
{
hashSet.add(nums[i]);
}
//开始寻找最长连续序列的长度
int maxLen=0;
for(int e:hashSet)
{
if(!hashSet.contains(e-1)) //当前的数字不存在前驱,是一个连续序列的首个数字
{
int len=0;
for(int i=e;;i++)
{
if(hashSet.contains(i))
{
len++;
}
else
{
break;
}
}
maxLen=Math.max(maxLen,len);
}
}
return maxLen;
}
}
时间复杂度:O(n)
空间复杂度:O(n)
基于哈希表的方法2
记录哈希表中的数字是否已经被遍历过,如果数字x
被遍历过,表示它所在的连续序列已近被搜索过不需要再搜索一遍,直接跳过x
;如果x
没有被遍历过,则开始搜索x
所在的连续序列的长度,和上面的只向后搜索不同,这里需要对x
本身,和它左右两边的数字都进行搜索并记录它们已经被遍历过,确定x
所在连续序列长度
基于哈希表方法2:
class Solution {
public int longestConsecutive(int[] nums) {
if(nums.length<=1)
{
return nums.length;
}
//存储nums中去重后的元素,并记录它们是否已经被寻找过
HashMap<Integer,Boolean> hashMap=new HashMap<>();
for(int i=0;i<nums.length;i++)
{
hashMap.put(nums[i],false);
}
//开始寻找最长最长连续序列的长度
int maxLen=0;
for(int k:hashMap.keySet())
{
if(!hashMap.get(k)) //还没有被寻找过,表示k所在的连续序列还没有被找过
{
int len=0;
for(int i=k-1;;i--) //寻找连续序列中位于k前面的数字
{
if(hashMap.containsKey(i))
{
hashMap.put(i,true);
len++;
}
else
{
break;
}
}
for(int i=k;;i++) //寻找连续序列中位于k后面的数字
{
if(hashMap.containsKey(i))
{
hashMap.put(i,true);
len++;
}
else
{
break;
}
}
maxLen=Math.max(maxLen,len);
}
}
return maxLen;
}
}
时间复杂度:O(n)
空间复杂度:O(n)