当前位置: 首页 > news >正文

LeetCode 热题 100——哈希——最长连续序列

3. 最长连续序列

题目描述

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

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

示例 1:

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

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
示例 3:

输入:nums = [1,0,1,2]
输出:3

提示:

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

求解

(1)去重 + 排序 + 遍历

var longestConsecutive = function(nums) {if (nums.length === 0) return 0// 思路: 排序 + 遍历// 一开始没考虑到重复序列,对于重复序列 [1,2,2,3] -> 3,排序的时候要去重nums.sort((a, b) => a - b); nums = [...new Set(nums)].sort((a, b) => a - b)let count = 0let max_count = 1 // 至少为1for(let i = 0; i < nums.length - 1; i++) {if(nums[i] + 1 === nums[i + 1]) {count++max_count = Math.max(max_count, count + 1)} else {count = 0}}return max_count
};

真的是习惯了复制粘贴的世界,自己敲代码百般漏洞(哭死);之前学的也全忘了,真的是每一处都是细节,虽然有点思路,但写出来s一样。
上面是自己的思路,也可用哈希求解
(2)使用Set

var longestConsecutive = function(nums) {if (nums.length === 0) return 0;let set = new Set(nums) // 去重let ans = 1 // 至少有一个for (let item of set) {// 找最小值if(!set.has(item - 1)) {// 满足条件,则为 该数组中 某一连续序列的最小值let count = 0 // 计数器let cur = item // 记录之后的连续值while(set.has(cur + 1)) {// while(set.has(cur)) {count++cur++}// while循环结束,说明 某一段连续序列已经遍历完成ans = Math.max(ans, count + 1)// ans = Math.max(ans, count)}}return ans
}

(3)两种解法的时间复杂度分析:

  • 排序 + 遍历(O (N log N)
    • sort() 排序的时间复杂度是 O(N log N)(JavaScript 的 sort 用 Timsort 算法);
    • 后续遍历数组是 O(N),但整体被排序的 O(N log N) 主导。
    • 为什么慢:排序本身就是一个耗时的操作,尤其是数据量大的时候。
  • Set 查找(O (N)
    • 用 Set 存储数据,has() 查找是 O(1)(哈希表特性)
    • 外层 for 循环遍历所有元素(O (N)),但内层 while 循环不会重复遍历同一个连续序列(例如,找到 1 后,会一次性遍历 1,2,3,4,后续再遇到 2,3,4 时会直接跳过)。
    • 整体下来,所有元素只会被遍历一次,总时间复杂度 O(N)。
    • 为什么快:用空间换时间,避免了排序,改用哈希表快速查找来确定连续序列。

补充知识点

sort()

Array.prototype.sort() 默认按 “字符串 Unicode 编码顺序” 排序,而非数字大小。它会先将数组中所有元素转换为字符串,再逐个比较字符的 Unicode 编码值:

  • 转换后字符串为 ["100", "4", "200", "1", "3", "2"]
  • 比较规则:先比第一个字符的编码(“1” < “2” < “3” < “4”),第一个字符相同时再比后续字符(如 “100” 与 “1”,第一个字符都是 “1”,但 “1” 长度更短,所以排在前面)。

因此,排序后的顺序是按字符串编码排列的,而非数字逻辑上的大小顺序。

  • 如何实现数字升序 / 降序?

    • 需手动传入比较函数 (a, b) => a - b(升序)或 (a, b) => b - a(降序):
    // 数字升序(正确排序)
    nums.sort((a, b) => a - b); 
    // 结果:[1, 2, 3, 4, 100, 200]// 数字降序
    nums.sort((a, b) => b - a); 
    // 结果:[200, 100, 4, 3, 2, 1]
    

去重 + 排序

  • Set 去重 + 排序(推荐)

    const nums = [100, 4, 200, 1, 3, 2, 2, 100];// 一行实现:去重 + 数字升序
    const result = [...new Set(nums)].sort((a, b) => a - b);console.log(result); // [1, 2, 3, 4, 100, 200]
    

    核心简化点:
    用扩展运算符 […new Set(nums)] 直接将 Set 转为数组(替代 Array.from),代码更紧凑;
    排序时直接传入数字比较函数,无需额外变量。

  • filter 去重 + 排序

    const nums = [100, 4, 200, 1, 3, 2, 2, 100];// 简化为一行 filter + 排序
    const result = nums.filter((v, i, self) => self.indexOf(v) === i).sort((a, b) => a - b);console.log(result); // [1, 2, 3, 4, 100, 200]
    

    去掉中间变量,直接链式调用 filter 和 sort; 箭头函数简化 filter 的回调逻辑。

  • Map 去重 + 排序(适合复杂场景)

    const nums = [100, 4, 200, 1, 3, 2, 2, 100];// 简化 Map 去重逻辑,一行转换为数组
    const result = Array.from(new Map(nums.map(v => [v, true])).keys()).sort((a, b) => a - b);console.log(result); // [1, 2, 3, 4, 100, 200]
    

    用 nums.map(v => [v, true]) 直接生成 Map 的键值对数组,替代 forEach 循环;
    链式调用 Array.from 和 sort,缩减代码行数。

总结:日常开发首选:[…new Set(nums)].sort((a, b) => a - b)(最简洁、性能最优)

== 和 ===

核心区别在于:是否进行类型转换。

  • === 严格相等 (Strict Equality)
    比较规则:
    先比较两边的值的 类型,如果类型不同,直接返回 false。
    如果类型相同,再比较它们的 值 是否相等。
    优点:行为可预测,不会产生意想不到的结果,是推荐的比较方式。
    示例:

    console.log(5 === 5);              // true (类型相同,值相同)
    console.log('5' === 5);            // false (类型不同:字符串 vs 数字)
    console.log(true === 1);           // false (类型不同:布尔值 vs 数字)
    console.log(null === undefined);   // false (类型不同:null vs undefined)
    console.log([] === []);            // false (类型相同,但引用的是不同的数组对象)
    
  • == 宽松相等 (Loose Equality)
    比较规则:
    先检查两边的值的类型,如果类型相同,就和 === 一样比较值。
    如果 类型不同,它会尝试进行 类型转换,将两边的值转换为同一个类型后再进行比较。
    缺点:类型转换的规则非常复杂,容易导致难以理解和调试的错误。
    示例:

    console.log(5 == 5);               // true (类型相同,值相同)
    console.log('5' == 5);             // true (字符串 '5' 被转换为数字 5)
    console.log(true == 1);            // true (布尔值 true 被转换为数字 1)
    console.log(null == undefined);    // true (这是一个特例,它们互相等于)
    console.log([] == '');             // true (数组 [] 转换为字符串是 '', 所以 '' == '' 为 true)
    console.log([] == 0);              // true (数组 [] -> 字符串 '' -> 数字 0)
    console.log('' == 0);              // true (字符串 '' 转换为数字 0)
    

    可以看到,== 的比较结果有时会让人感到困惑。

  • 核心区别对照表

特性=== 严格相等== 宽松相等
类型检查先比较类型,类型不同则为 false类型不同时,会尝试将值转换为同一类型
结果可预测性高,行为清晰低,转换规则复杂,易出错
推荐使用场景几乎所有场景仅在某些特定、明确需要利用其类型转换规则的情况下使用
null vs undefinednull === undefined 为 falsenull == undefined 为 true (唯一特例)

Set

Set 是一种内置对象,用于存储唯一值的集合(即集合中的元素不会重复)。它的核心特点和用法如下:
1.基本概念
Set 是 ES6 新增的结构,以值 - 值的形式存储数据(与 Map 的 “键 - 值” 不同),且元素唯一(重复添加同一值会被自动忽略)。
2.常用方法

方法功能描述
new Set()创建一个空的 Set 实例。
set.add(value)向 Set 中添加一个值,返回 Set 本身(可链式调用)。
set.delete(value)删除指定值,返回布尔值(成功删除为 true,否则 false)。
set.has(value)判断 Set 中是否存在指定值,返回布尔值(true/false)。
set.clear()清空 Set 中所有值。
set.size只读属性,返回 Set 中值的数量。

3.遍历方式
Set 是可迭代对象,支持以下遍历方式:

  • for...of 遍历:for (let value of set) { … }
  • set.keys():遍历所有值(与 set.values() 效果一致,因为 Set 是 “值 - 值” 结构)。
  • set.entries():遍历所有值的 “键值对”(但键和值相同,如 [1, 1])。

4.与数组的区别

特性Set数组(Array)
元素唯一性元素唯一(重复值会被忽略)允许重复元素
遍历便利性原生支持迭代(for…of)需手动遍历或用 forEach
去重操作天生去重,直接创建 Set 即可需借助 filter 或 Set 转换(如 […new Set(arr)])

5.代码示例

// 创建 Set
const set = new Set();// 添加值(可链式调用)
set.add(1).add(2).add(2); // 重复的 2 会被忽略
console.log(set); // Set { 1, 2 }// 检查是否存在值
console.log(set.has(2)); // true
console.log(set.has(3)); // false// 删除值
set.delete(1);
console.log(set); // Set { 2 }// 遍历
for (let value of set) {console.log(value); // 2
}// 转换为数组(常用去重方式)
const arr = [1, 2, 2, 3];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3]

6.典型应用场景

  • 数组去重:[...new Set(arr)] 是最简洁的数组去重方式。
  • 判断元素是否重复:利用 Set 的唯一性,可快速检查数据中是否存在重复项。
  • 交集、并集、差集计算:结合 Set 的遍历和方法,可高效实现集合运算(如 const intersection = new Set([...set1].filter(x => set2.has(x))))。
http://www.dtcms.com/a/602181.html

相关文章:

  • c语言反编译软件|详细解析c语言反编译工具的使用及其重要性
  • 模板网站更改青海制作网站的公司
  • 牛客:栈的压入、弹出序列
  • 深入解析UDP服务器核心开发机制
  • 阜阳做网站的公司网站开发前端跟后端的区别
  • MongoDB知识点与技巧总结
  • 企业网站 设计国外免费建站网站不用下载
  • LeetCode算法学习之数组中的第K个最大元素
  • 应急调度系统让每一次救援都精准到位
  • RL机器人人库使用简介
  • 北京网络公司建站百度爱采购优化排名软件
  • 长沙微网站开发重庆网站建设及优化公司
  • Java集合框架深度剖析 — 从源码看ArrayList、HashMap的设计与优化
  • 网站关键词排名快速提升thinkphp建站网址
  • JavaScript中??、、||、?.运算的区别
  • 微信公众号网站怎么做上海网站开发报价
  • Python压缩音乐文件大小
  • 用什么软件写网站荷城网站设计
  • 长治哪家公司做网站好怎么做网站教程视频
  • 跟踪导论(三)——滤波的释义位置信息的“观测+修正”
  • 一个电商网站开发周期是多久搜索引擎营销流程是什么?
  • 计算机做网站难吗网站建设费可以走办公费吗
  • 做公众号还是网站建网站哪家划算
  • 从App时代到智能体时代,如何打破“三堵墙”
  • jsp怎么拿到url参数
  • 有机蔬菜:清爽解腻的炖锅搭档
  • 网站的时间对齐应该怎么做wordpress中文评论插件
  • 515ppt网站建设岳麓区专业的建设网站公司
  • mysql第5次作业---hyx
  • LLM的“哥白尼革命”:物理AI与世界模型,AI的下一个战场!