题目描述
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
解题思路
算法,数据结构
并查集,哈希表
思路
把连续的数字在并查集中 merge,然后统计最长的序列
过程
拿到一个并查集实例(u),哈希表实例(map)
遍历所有的数字,在遍历过程中:
记录每个数字在 nums 中的位置(index),如果是第二次遍历到相同数字,直接忽略它
如果哈希表中记录了这个数字,并且记录了比这个数字大1,小1的数字的 index,
那么需要在并查集中进行 merge:
如果当前数字是 x,索引是 i
- 哈希表记录了
x - 1的index,并查集(u)merge(map[x - 1], i) - 哈希表记录了
x + 1的index,并查集(u)merge(map[x + 1], i)
另外,我们还在并查集中声明一个数组 cnt 作为实例的属性,它的作用是:
记录每个集合作为 root 时,他的集合元素个数是多少,这是我们解决此题的关键一步
因此在 merge 之前,我们需要合并两个集合的个数
最终,当我们遍历完所有元素的时候,我们需要返回一个最大的 cnt 项
代码
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function (nums) {
const map = {}
const u = new UnionSet(nums.length)
for (let i = 0; i < nums.length; i++) {
const x = nums[i]
if (map[x] === undefined) {
if (map[x - 1] !== undefined) {
u.merge(map[x - 1], i)
}
if (map[x + 1] !== undefined) {
u.merge(map[x + 1], i)
}
map[x] = i
}
}
let ans = 0
for (let i = 0; i < nums.length; i++) {
if (i === u.get(i) && (u.cnt[i] > ans)) {
ans = u.cnt[i]
}
}
return ans
}
class UnionSet {
constructor(n) {
this.fa = new Array(n + 1).fill(0).map((item, index) => index)
this.cnt = new Array(n + 1).fill(1)
}
get(x) {
return (this.fa[x] = this.fa[x] === x ? x : this.get(this.fa[x]))
}
merge(a, b) {
if (this.get(a) === this.get(b)) return
this.cnt[this.get(b)] += this.cnt[this.get(a)]
this.fa[this.get(a)] = this.get(b)
}
}