题目
给定一个未排序的整数数组 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