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

一、前置知识(可跳过)

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

实列与展示

四、结语

再见!

相关推荐
玲小珑20 分钟前
Auto.js 入门指南(六)多线程与异步操作
android·前端
白瓷梅子汤22 分钟前
跟着官方示例学习 @tanStack-table --- Header Groups
前端·react.js
喝牛奶的小蜜蜂26 分钟前
个人小程序:不懂后台,如何做数据交互
前端·微信小程序·小程序·云开发
front_explorers31 分钟前
Umi项目必看,从Webpack到Rspack,KMI引领性能革命🚀
前端
旺仔牛仔QQ糖32 分钟前
都写那么多项目了, 傻傻分不清楚NODE_ENV 和 模式(Mode) 两者区别是什么
前端·面试
xcLeigh37 分钟前
HTML5实现简洁的体育赛事网站源码
前端·html
渔舟唱晚@40 分钟前
Axios 取消请求的演进:CancelToken vs. AbortController
javascript
棉花糖超人40 分钟前
【从0-1的CSS】第1篇:CSS简介,选择器已经常用样式
前端·css
GISer_Jing1 小时前
XHR / Fetch / Axios 请求的取消请求与请求重试
前端·javascript·网络
天涯学馆1 小时前
微前端架构设计:从理论到实践的全面指南
前端·javascript·面试