Skip to content
On this page

题目描述

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

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

plaintext
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

思路

方法一:哈希表

这个思路比较简单,主要精力在优化上。初始想法是遍历数组,然后对于每个元素,去查看num+1,num+2…是否在数组中,这样的时间复杂度是O(n^3)(数组查找元素也有O(n)的复杂度)。
优化:

  1. 使用哈希表来存储数组中的元素,这样查找的时间复杂度就是O(1)了。
  2. 对于每个元素,只有当它的前一个元素不在数组中时,才去查找后面的元素,这样可以减少很多不必要的查找。也就是说,只有当num-1不在数组中时,才去查找num+1,num+2…是否在数组中。
javascript
/**
 * @param {number[]} nums
 * @return {number}
 */
var longestConsecutive = function (nums) {
  const set = new Set(nums)

  let max = 0
  let cur = 0

  for (let n of set) {
    if (!set.has(n - 1)) {
      let tmp = n
      while (set.has(tmp)) {
        cur++
        tmp += 1
      }
      max = Math.max(max, cur)
      cur = 0
    }
  }
  return max
}

方法二:动态规划

这个方法是我最开始想到的能控制时间复杂度在O(n)的方法,但是我没有想到具体怎么实现,所以就没写出来。这个方法的思路是,使用一个map维护每个元素的最长连续序列长度,然后遍历数组,对于每个元素,查看num-1和num+1的最长连续序列长度,然后更新num-1和num+1的最长连续序列长度。这样的时间复杂度是O(n)。

javascript
/**
 * @param {number[]} nums
 * @return {number}
 */
var longestConsecutive = function (nums) {
  const map = new Map()
  let max = 0

  for (let num of nums) {
    if (!map.has(num)) {
      let left = map.get(num - 1) || 0
      let right = map.get(num + 1) || 0

      let len = 1 + left + right
      max = Math.max(max, len)
      map.set(num, len)
      map.set(num - left, len)
      map.set(num + right, len)
    }
  }
  return max
}

方法三:并查集

并查集这个概念涉及到知识盲区了,需要补补课。

javascript
/**
 * @param {number[]} nums
 * @return {number}
 */
class UnionFind {
  constructor(nums) {
    this.parent = new Map()
    this.size = new Map()
    for (let num of nums) {
      this.parent.set(num, num)
      this.size.set(num, 1)
    }
  }

  find(x) {
    if (this.parent.get(x) === x) return x
    this.parent.set(x, this.find(this.parent.get(x)))
    return this.parent.get(x)
  }

  union(x, y) {
    let rootX = this.find(x)
    let rootY = this.find(y)
    if (rootX === rootY) return
    this.parent.set(rootX, rootY)
    this.size.set(rootY, this.size.get(rootX) + this.size.get(rootY))
  }

  getSize(x) {
    return this.size.get(this.find(x))
  }
}

var longestConsecutive = function (nums) {
  const uf = new UnionFind(nums)
  const map = new Map()
  let max = 0

  for (let num of nums) {
    if (map.has(num)) continue
    map.set(num, num)
    if (map.has(num - 1)) uf.union(num, num - 1)
    if (map.has(num + 1)) uf.union(num, num + 1)
    max = Math.max(max, uf.getSize(num))
  }
  return max
}

Released under the MIT License.