[路飞] 最长连续序列

211 阅读1分钟

题目描述

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

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

解题思路

算法,数据结构

并查集,哈希表

思路

把连续的数字在并查集中 merge,然后统计最长的序列

过程

拿到一个并查集实例(u),哈希表实例(map)

遍历所有的数字,在遍历过程中:

记录每个数字在 nums 中的位置(index),如果是第二次遍历到相同数字,直接忽略它

如果哈希表中记录了这个数字,并且记录了比这个数字大1,小1的数字的 index
那么需要在并查集中进行 merge

如果当前数字是 x,索引是 i

  • 哈希表记录了 x - 1index,并查集(u) merge(map[x - 1], i)
  • 哈希表记录了 x + 1index,并查集(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)

    }
  }