LeetCode 最长连续序列

111 阅读3分钟

最长连续序列

Leetcode 做题笔记,就当作记录了。我的算法也不好,就不发到 Leetcode 评论,去误人子弟了。有问题也欢迎讨论,敬请斧正。

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

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

例如 nums = [100,4,200,1,3,2]

最长序列就是: 1 2 3 4 长度 4

想法是使用 hashMap,记录数组中每个数字对应的最长字符串长度。如果每个都遍历一边,就很啰嗦,需要尽量保存之前遍历过的结果。

很容易想到,当遍历到某个数字时候,使用前继数字长度加一,就无需遍历前面的数字了。如果数组都是升序,只需要遍历一边就可以了。存在降序情况,就需要更新之前的数据。这样才能保证,加一符合当前情况。

下面是我最开始想出来的算法:

// 最长连续序列
function main(arr) {
  let max = 0; // 最大长度,默认空数组
  const lengthMap = new Map();
  for (let i = 0; i < arr.length; i++) {
    let num = arr[i];
    // 跳过重复项
    if (lengthMap.has(num)) continue;
    // 前继加一
    let prevCount = lengthMap.has(num - 1) ? lengthMap.get(num - 1) + 1 : 1;
​
    lengthMap.set(num, prevCount);
​
    while (lengthMap.has(++num)) {
      // 查找上一个count
      // 降序部分,如果已处理过,不能丢失之前的数据
      // 当前 count,可能在已计算过
      let nextCount = lengthMap.get(num);
      // 下一个数的 count 小于等于上一个
      // 证明遍历中间有断层,以小的数字为准
      prevCount = nextCount <= prevCount ? prevCount + 1 : nextCount;
      lengthMap.set(num, prevCount);
    }
    max = max > prevCount ? max : prevCount; // 更新长度
  }
  return max;
}

大部分用例都过了,挂在了一个数据量比较大的用例上,超时了。

重新审视算法,发现有些浪费的地方。这个算法把每个数字对应的连续序列都计算了出来,实际上并没有必要。根据题目要求,只需要找到最大的序列就行了。这样就变成了找到每个序列最小的数字,从小往大查找一遍,再比较每个序列的大小就行了。

当前的遍历情况,不能确定当前数字是不是序列的最小值,每一次出现序列中的更小值,都要遍历一遍更新,降序的时间复杂度 O(n2)。

最简单的优化方式就是排序,排完序就不需要考虑升降序问题了。但是我这里写了 map,就还想沿着这个思路走下去。

map 的优化思路也很明确,想办法确定当前值是否为序列最小值。如果有简单办法检索,当前数组中是否有比当前值小 1 的数(例如,当前为 2,需要知道是否有 1 存在),那样就能保证一次把连续序列遍历完。

下面是优化后的代码,和官方答案思路是一致的(本来也是参考了官方答案):

function main(arr) {
  let max = 0;
  // 使用 Object 也可以
  // 这里不需要存储 index 或者值
  // 设置 Object 对应项 value 为 1 就行
  // 遇到数字,Object 总要额外判断 0 很麻烦
  // 所以我习惯用 map
  const valueMap = new Map();
  for (const val of arr) {
    if (!valueMap.has(val)) {
      // 重复值跳过
      valueMap.set(val, 1);
    }
  }
  // 遍历 map,map 已经做过去重
  valueMap.forEach((_, val) => {
    if (!valueMap.has(val - 1)) {
      let len = 1;
      let num = val;
      // 最小值开始查找
      // 同一条序列只会遍历一次
      // 这样每个数字也只会遍历一次
      while (valueMap.has(num + 1)) {
        len++;
        num++;
      }
      max = Math.max(max, len);
    }
  });
  return max;
}