LeetCode 128-最长连续序列

126 阅读3分钟

题目

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

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

  示例 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 <= 105
  • -109 <= nums[i] <= 109

思路

解法一:哈希表+排小查大原则

基础解题思路: 为了避免重复查询,先对nums进行去重。然后遍历nums的每一个数字,查询数字的num+1是否在nums中,循环查询一直到nums+n不在数组中,得到长度为l,然后获取到最大的l为结果。

为什么不查询num-1是否在数组中?

因为num!=num-1,所以num-1其实在这个过程中也会被遍历到,并不是说就不会遍历到,所以不会影响结果。

代码一: 循环判断num+1是否在区间内

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        nums_set = set(nums)
        if len(nums_set) < 2:
            return len(nums_set)
        res = 1
        for num in nums_set:
            cur = num
            count = 1
            while cur + 1 in nums_set:
                cur += 1
                count += 1
            res = max(res, count)
        return res

发现上面代码在提交时,出现了超时。

在上面的问题中,不难发现既然num-1会被遍历到,再次遍历num其实是一种重复计算。那么如果才能避免重复查找呢?

假设nums中的某个值num,在数组中连续最长长度为[num-i, ... ,num+j],那么综上所述,对[num-i+1, ... ,num+j]的遍历都是浪费资源的,观察这个区间内,区间内的任意元素num, num-1的值都在列表中。

代码二: num-1不在区间再循环判断num+1

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        nums_set = set(nums)
        if len(nums_set) < 2:
            return len(nums_set)
        res = 1
        for num in nums_set:
            if num - 1 not in nums_set:
                cur = num
                count = 1
                while cur + 1 in nums_set:
                    cur += 1
                    count += 1
                res = max(res, count)
        return res

解法二: 哈希表+并查集

为了避免重复计算,对nums的数字进行去重。对每个元素进行分组,将相邻的元素连接在一起。将num和num+1连接在一起,然后对num的每个元素进行合并,查找最大的长度。

并查集有两个主要的方法:find路径查找,union合并2个节点。

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        nums_set = set(nums)
        if len(nums_set) < 2:
            return len(nums_set)
        uf = UnionFind(nums_set)
        for num in nums_set:
            uf.union(num, num+1)
        
        res = 0
        for num in nums_set:
            # 判断num-1是否在,避免重复计算
            if num-1 not in nums_set:
                res = max(res, uf.find(num) - num + 1)
        return res


class UnionFind:
    """并查集"""
    def __init__(self, nums):
        self.parents = {}
        for num in nums:
            self.parents[num] = num
    
    def find(self, x):
        if self.parents.get(x) == x:
            return x
        self.parents[x] = self.find(self.parents.get(x))
        return self.parents[x]

    def union(self, x, y):
        if y in self.parents:
            self.parents[x] = y