LeetCode 128.最长连续序列

175 阅读1分钟

「这是我参与2022首次更文挑战的第38天,活动详情查看:2022首次更文挑战」。

题目:给定一个没有排序的数组nums,要求找出数字连续的最长子序列的长度。要求实现的时间复杂度为O(n)O(n)

例如输入的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;//返回最终的结果
    }
}

但该方式的时间复杂度不满足题目要求的O(n)O(n),因此考虑换一种思路。

哈希表存储元素

题目中所给的数组有时可能会包含大量重复元素,此时考虑将数组中元素存入哈希表,这样就能避免上述方法的相等字符需要跳跃的问题。并且遍历数组的每一个元素,可以利用哈希表来判断当前元素是否属于子序列的边界,如果是则执行下一次循环,开始下一个元素的判断。如果不是则此时的元素即为一个边界,在哈希表中查找当前元素不断自增后的元素是否在哈希表内,如果存在则子序列长度也自增,最后更新最长子序列长度即可,可得代码如下:

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;
    }

上述代码时间复杂度为O(n)O(n),空间复杂度也为O(n)O(n)。但代码最终耗时337ms,导致耗时如此长的原因是在上面代码中遍历的是nums数组,因为示例数组可能包含大量重复元素,因此我们只需要遍历set集合即可,即将第二个for循环改为:

for(int element:set){
            if(set.contains(element-1)){
                continue;
                ...
                }
          }

此时仅仅耗时12ms