【LeetCode-中等】128. 最长连续序列(详解)

题目

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

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-consecutive-sequence

题目分析

灵感来源:

作者:yimeixiaobai
链接:https://leetcode.cn/problems/longest-consecutive-sequence/solution/xiao-bai-lang-ha-xi-ji-he-ha-xi-biao-don-j5a2/

看到本题找最长连续序列,每个人都会想到两种最朴素的解法之一:

1.先排序,从前往后找最长连续上升序列即可。该思路简单有效,但是复杂度已经至少有O(nlogn)O(nlogn)。实现起来也比较简单,在此不讨论该解法。

2.遍历数组中的每个元素num,然后以num为起点,每次+1向后遍历num+1,num+2,num+3...,判断这些元素是否存在于数组中。假设找到的最大的连续存在的元素为num+x,那么这个连续序列的长度即为x+1。最后将每个num所开始序列长度取个最大值即可。
这个思路很通俗易懂,实现成代码,如下所示:

class Solution {
    public int longestConsecutive(int[] nums) {
        int n = nums.length;
        int ans = 0;
        // 遍历数组中的每个元素num
        for (int i = 0; i < n; i++) {
            // 以num为起点,每次+1向后遍历num+1,num+2,num+3...
            int cur = nums[i] + 1;
            while (true) {
                // 标识是否在数组中找到了cur
                boolean flag = false;
                // 在数组中找cur
                for (int j = 0; j < n; j++) {
                    if (nums[j] == cur) {
                        flag = true;
                        break;
                    }
                }
                if (!flag) {
                    break;
                }
                cur += 1;
            }
            ans = Math.max(ans, cur - nums[i]);
        }
        return ans;
    }
}

方法1:哈希集合

上面的代码是超时的,我们将它优化成不超时的。
(1)用HashSet代替直接遍历,可以降低复杂度,而且可以去重复。
(2)上面的方法的缺点:比如元素[1,2,4,3,5],上面的方法是:先选择1,遍历数组查找数组中有没有1+1=2,有的话再查找有没有2+1=3,以此查找到了5,然后再选择2,查找数组中有没有3,以此查找到5,这里就能看出出现了重复的代码,在选择1时,我们查找了2,3,4,5,,而选择2时,我们又查找了3,4,5,这样就重复了。

我们的改进:遍历数组中每个元素num。逐一遍历每个元素会产生很多冗余工作,实际上我们无需一次针对每个元素num去判断num+1,num+2,num+3...是否在数组中。如果num-1已经在数组中的话,那么num-1肯定会进行相应的+1遍历,然后遍历到num,而且从num-1开始的+1遍历必定比从num开始的+1遍历得到的序列长度更长。因此,我们便可将在一个连续序列中的元素进行删减,让其只在最小的元素才开始+1遍历,也就是:只有当num-1不存在时,才开始向后遍历查找num+1,num+2,num+3

比如,现有元素[1,2,4,3,5],当2,3,4,5发现均有比自己小1的元素存在,那么它们就不会开始+1遍历,而1是连续序列中最小的元素,没有比自己小1的元素存在,所以会开始+1遍历。通过上述方式便可将时间复杂度优化至O(n)。

class Solution {
    public int longestConsecutive(int[] nums) {
        // 建立一个存储所有数的哈希表,同时起到去重功能
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }

        int ans = 0;
        // 遍历去重后的所有数字
        for (int num : set) {
            int cur = num;
            // 只有当num-1不存在时,才开始向后遍历num+1,num+2,num+3......
            if (!set.contains(cur - 1)) {
                //查看数组中是否有 cur+1 ,有的话就继续查找 cur +1 +1
                while (set.contains(cur + 1)) {
                    cur++;
                }
            }
            // [num, cur]之间是连续的,数字有cur - num + 1个
            ans = Math.max(ans, cur - num + 1);
        }
        return ans;
    }
}

 这个方法就是对上面超时方法的一些改进,好理解

感悟:当我们拿到一个题时,没有头绪时,不妨先写写你知道这个方法会超时的方法,然后思考如何将其改进,而不是一上来就想难的方法。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值