力扣解题-最长连续序列

4 阅读6分钟

力扣解题-最长连续序列

题目: 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

示例 1: 输入:nums = [100,4,200,1,3,2] 输出:4 解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2: 输入:nums = [0,3,7,2,5,8,4,6,0,1] 输出:9

提示: 0 <= nums.length <= 104 -109 <= nums[i] <= 109 进阶:可以设计并实现时间复杂度为 O(n) 的解决方案吗?

注意:本题与主站 128 题相同: leetcode.cn/problems/lo…

Related Topics 并查集 数组 哈希表


第一次解答(失败,超时)

解题思路

核心方法:哈希表存储所有元素 + 遍历原数组逐个向前查找连续序列,利用哈希表O(1)的查找效率,尝试通过遍历每个元素并向前查找num-1、num-2...的方式,统计以当前元素为终点的连续序列长度,最终取所有长度的最大值。

具体步骤:

  1. 初始化HashMap,将数组中所有元素以「数值-数值」的键值对形式存入,仅用于快速判断某个数值是否存在。
  2. 初始化结果变量res为0,用于记录最长连续序列的长度。
  3. 遍历原数组nums的每个元素nums[i]: a. 初始化当前序列长度curentRes为0,当前查找值valuenums[i]。 b. 通过while循环不断查找哈希表中是否包含value,若包含则当前长度加1,且value自减1(向前查找连续数)。 c. 循环结束后,更新resrescurentRes中的较大值。
  4. 遍历完成后返回res

失败原因分析

该解法超时的核心原因是存在大量重复且无效的计算,时间复杂度退化为O(n²),无法满足题目数据规模要求,具体问题点:

  1. 未去重导致重复遍历:哈希表存储了所有元素,但遍历的是原数组而非去重后的集合,若数组存在重复数值(如示例2中的0),会对同一个数值多次执行完整的连续序列查找,产生大量冗余计算。
  2. 未跳过非序列起点元素:对数组中每个元素都执行向前查找的完整循环,即使该元素是某连续序列的中间节点(如序列[1,2,3,4]中的2、3、4),也会重新从该元素开始查找1、0...,同一个连续序列会被多次遍历,最坏情况下(如数组是连续递增序列),时间复杂度会达到O(n²),对于10^4级别的数组,直接触发超时。
  3. 哈希表无意义存储:以num-num为键值对存储,仅用了哈希表的containsKey功能,完全可替换为更轻量的HashSet,属于不必要的内存开销。
public int longestConsecutive(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int num:nums){
            map.put(num,num);
        }
        int res = 0;
        for(int i=0;i<nums.length;i++){
            int curentRes=0;
            int value=nums[i];
            while(map.containsKey(value)){
                curentRes=curentRes+1;
                value=value-1;
            }
            res=Math.max(res,curentRes);
        }
        return res;
    }

第二次解答

解题思路

核心方法:HashSet去重 + 仅从连续序列起点开始向后遍历,解决了第一次解答的重复计算问题,确保每个元素仅被访问有限次,将时间复杂度优化至O(n),同时利用Set的轻量特性减少内存开销。

具体步骤:

  1. 初始化HashSet,将数组中所有元素存入集合,自动完成去重,避免重复数值的无效计算,同时保留O(1)的查找效率。
  2. 初始化最大长度变量maxLen为0,用于记录最长连续序列的长度。
  3. 遍历去重后的Set而非原数组,对每个数值num执行核心判断: a. 先判断Set中是否不包含num - 1,该条件是连续序列起点的唯一判定依据(若包含num-1,说明num是某连续序列的中间节点,无需处理)。 b. 若num是序列起点,初始化当前序列数值currentNumnum,当前序列长度currentLen为1(起点自身占1个长度)。 c. 通过while循环向后查找连续数currentNum + 1),若Set中包含则currentNum自增1、currentLen加1,直到无法找到下一个连续数。 d. 循环结束后,更新maxLenmaxLencurrentLen中的较大值。
  4. 遍历完成后,返回maxLen即为最长连续序列的长度。

核心优化点说明

  1. Set去重:消除原数组重复数值的重复计算,确保每个数值仅被处理一次。
  2. 起点判定:通过!set.contains(num - 1)跳过所有连续序列的中间/终点元素,仅对每个连续序列的起点执行一次向后遍历,从根本上解决了第一次解答的重复遍历问题。
  3. 向后查找更高效:贴合连续序列的特性,从起点向后查找num+1,每个元素最多被访问两次(一次是遍历到自身,一次是作为连续数被起点查找),整体时间复杂度稳定为O(n)。
  4. 轻量存储:用HashSet替代HashMap,仅存储数值完成存在性判断,减少内存开销。

执行耗时:13 ms,击败了95.38% 的Java用户 内存消耗:58.9 MB,击败了45.88% 的Java用户

public int longestConsecutive(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for(int num:nums){
            set.add(num);
        }
        int maxLen = 0;
        for (int num : set) { 
            if (!set.contains(num - 1)) {
                int currentNum = num;
                int currentLen = 1;

                while (set.contains(currentNum + 1)) {
                    currentNum++;
                    currentLen++;
                }

                maxLen = Math.max(maxLen, currentLen);
            }
        }
        return maxLen;
    }

总结

  1. 第一次解答失败的核心是无去重+无起点判定,导致大量重复计算,时间复杂度退化为O(n²),触发超时;同时存在哈希表无意义存储的内存开销。
  2. 第二次解答的核心优化是HashSet去重仅从序列起点遍历,从根本上消除了重复计算,将时间复杂度优化至O(n),满足题目进阶要求。
  3. 两次解法均利用了哈希结构的O(1)查找效率,第二次通过精准判定遍历起点的小技巧,实现了从超时解法到高效解法的转变,是本题的解题关键。
  4. 本题的核心思路是空间换时间,用哈希集合的O(n)空间开销,换取遍历效率的质的提升,同时通过逻辑优化避免冗余计算。