一分钟解决 | 高频面试算法题——最长连续序列(哈希表)

一、前置知识(可跳过)

javascript中的Set

1. 高效的查找操作:

  • Sethas() 方法: Set 提供了 has(value) 方法来检查一个元素是否存在于集合中。 这个操作的平均时间复杂度是 O(1)。 这意味着无论 Set 中有多少个元素,检查一个元素是否存在的时间几乎是恒定的。
  • 相比之下,数组的 includes() 方法: 如果你使用数组而不是 Set,并且使用 includes() 方法来查找某个元素是否存在,那么 includes() 方法的时间复杂度是 O(n),其中 n 是数组的长度。 这意味着在最坏情况下,你需要遍历整个数组才能确定元素是否存在。

2. 避免重复元素:

  • Set 只存储唯一的元素。 如果你多次向 Set 中添加相同的元素,Set 会自动忽略重复的元素。 在寻找连续序列时,重复的数字不会影响结果,所以 Set 可以帮助你自动去重。

二、题目描述------最长连续序列

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

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

示例 1:

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

示例 2:

ini 复制代码
输入: nums = [0,3,7,2,5,8,4,6,0,1]
输出: 9

示例 3:

ini 复制代码
输入: nums = [1,0,1,2]
输出: 3

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109

三、题解

js 复制代码
/**
 * @param {number[]} nums
 * @return {number}
 */
var longestConsecutive = function(nums) {
    if (!nums || nums.length === 0) {
        return 0;
    }

    const numSet = new Set(nums); // 使用 Set 实现 O(1) 的查找效率
    let longestStreak = 0;

    for (const num of numSet) {
        // 只需从序列的起始数字开始计算长度
        if (!numSet.has(num - 1)) {
            let currentNum = num;
            let currentStreak = 1;

            // 沿着序列一直向下查找
            while (numSet.has(currentNum + 1)) {
                currentNum += 1;
                currentStreak += 1;
            }

            longestStreak = Math.max(longestStreak, currentStreak);
        }
    }

    return longestStreak;
};

核心思路

1.使用 Set 实现快速查找:

javascript 复制代码
-   `Set` 数据结构允许你以平均 O(1) 的时间复杂度检查一个元素是否存在。 这意味着,无论 Set 中有多少个元素,检查一个元素是否存在的时间几乎是恒定的。
-   通过将数组中的所有数字添加到 Set 中,我们可以避免使用 `includes()` 或其他线性查找方法,从而减少查找相邻数字的时间复杂度。

2.只从序列的起始数字开始计算:

markdown 复制代码
-   这个优化至关重要。 我们只从序列的起始数字开始计算序列的长度。 序列的起始数字指的是,它的前一个数字(`num - 1`)不存在于 Set 中的数字。
-   如果一个数字不是一个序列的起始数字,我们就可以跳过它,因为它肯定已经被包含在其他序列中。
-   通过只从序列的起始数字开始计算,我们确保每个数字只会被当做连续序列的一部分计算一次。这对于保证 O(n) 的时间复杂度至关重要。

详细解释

  1. 初始化:

    • 首先,处理空数组或 null 的情况,直接返回 0。
    • 创建一个 numSet (Set 对象):将数组中的所有数字添加到 Set 中。 Set 是一种特殊的数据结构,它允许我们以平均 O(1) 的时间复杂度检查一个元素是否存在。 这是解决时间复杂度问题的关键。
  2. 遍历 Set:

    • 遍历整个 numSet。 对于 Set 中的每个数字 num,我们检查它是否是一个序列的起始数字。
  3. 检查序列起始:

    • if (!numSet.has(num - 1)): 这是关键优化。 我们只从序列的起始数字开始计算序列长度。 如果 num - 1 存在于 numSet 中,那么 num 肯定不是一个序列的起始数字,因为 num - 1 才是。 如果当前数 num 不是一个序列的起始数字, 就跳过本次循环,处理下一个数字。 确保每个数字只会被当做连续序列的一部分计算一次, 这对于保证 O(n) 的时间复杂度至关重要。
  4. 计算序列长度:

    • 如果 num 是一个序列的起始数字,那么我们就开始沿着序列一直向下查找:

      • currentNum = num;: 将当前数字设置为该起始数字。
      • currentStreak = 1;: 初始化当前序列的长度为 1。
      • while (numSet.has(currentNum + 1)): 只要 currentNum + 1 存在于 numSet 中,就说明序列还在继续。
      • currentNum += 1;: 向下移动到序列中的下一个数字。
      • currentStreak += 1;: 增加序列的长度。
  5. 更新最长序列长度:

    • longestStreak = Math.max(longestStreak, currentStreak);: 在每次找到一个序列后,将当前序列的长度与 longestStreak 进行比较,并更新 longestStreak
  6. 返回结果:

    • 最后,返回 longestStreak,即最长连续序列的长度。

实列与展示

四、结语

再见!

相关推荐
滿1 分钟前
Vue3 + Element Plus 动态表单实现
javascript·vue.js·elementui
钢铁男儿8 分钟前
C# 方法(值参数和引用参数)
java·前端·c#
阿金要当大魔王~~8 分钟前
面试问题(连载。。。。)
前端·javascript·vue.js
蒟蒻小袁14 分钟前
力扣面试150题-- 翻转二叉树
算法·leetcode·面试
yuanyxh16 分钟前
commonmark.js 源码阅读(一) - Block Parser
开发语言·前端·javascript
养一只Trapped_beast17 分钟前
【LeetCode】删除排序数组中的重复项 II
算法·leetcode·职场和发展
矢鱼20 分钟前
单调栈所有模版(2)
算法
轮到我狗叫了27 分钟前
力扣智慧思想小题,目录力扣.跳跃游戏(思想很重要)力扣.跳跃游戏II(还是思想)力扣.分发糖果力扣151.反转字符串中的单词力扣.轮转数组
数据结构·算法·leetcode
进取星辰35 分钟前
22、城堡防御工事——React 19 错误边界与监控
开发语言·前端·javascript
zxctsclrjjjcph36 分钟前
【递归、搜索和回溯】递归、搜索和回溯介绍及递归类算法例题
开发语言·c++·算法·力扣