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

一、前置知识(可跳过)

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,即最长连续序列的长度。

实列与展示

四、结语

再见!

相关推荐
南风木兮丶几秒前
Vue 项目安装 @antfu/eslint-config 保姆级教程
前端·javascript·vue.js
IT成长日记8 分钟前
【LVS入门宝典】LVS调度算法轮询(RR)深度解析:从原理到实战的公平调度之道
算法·lvs·rr·轮询调度算法
万少14 分钟前
记 HarmonyOS 开发中的一个小事件 怒提华为工单
前端·harmonyos
未来之窗软件服务15 分钟前
万象EXCEL开发(六)excel单元格运算逻辑 ——东方仙盟金丹期
前端·excel·仙盟创梦ide·东方仙盟·万象excel
Mintopia22 分钟前
🚀 Cesium-Kit:10 秒为你的 Cesium 项目添加动态光效标记
前端·javascript·cesium
Mintopia24 分钟前
🌩️ 云边协同架构下的 WebAI 动态资源调度技术
前端·javascript·aigc
Olrookie26 分钟前
若依前后端分离版学习笔记(十六)——scoped、路由跳转
前端·笔记
qaqxiaolei26 分钟前
高效办公利器:前端实现表格导出excel格式 + 自定义水印的完整方案
前端·javascript
NAGNIP27 分钟前
一文搞懂量化、剪枝和知识蒸馏都是什么?
算法
叫我詹躲躲28 分钟前
为什么Bun.js能在3秒内启动一个完整的Web应用?
前端·javascript·bun