一、前置知识(可跳过)
javascript中的Set
1. 高效的查找操作:
Set
的has()
方法: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) 的时间复杂度至关重要。
详细解释
-
初始化:
- 首先,处理空数组或
null
的情况,直接返回 0。 - 创建一个
numSet
(Set 对象):将数组中的所有数字添加到 Set 中。 Set 是一种特殊的数据结构,它允许我们以平均 O(1) 的时间复杂度检查一个元素是否存在。 这是解决时间复杂度问题的关键。
- 首先,处理空数组或
-
遍历 Set:
- 遍历整个
numSet
。 对于 Set 中的每个数字num
,我们检查它是否是一个序列的起始数字。
- 遍历整个
-
检查序列起始:
if (!numSet.has(num - 1))
: 这是关键优化。 我们只从序列的起始数字开始计算序列长度。 如果num - 1
存在于numSet
中,那么num
肯定不是一个序列的起始数字,因为num - 1
才是。 如果当前数num
不是一个序列的起始数字, 就跳过本次循环,处理下一个数字。 确保每个数字只会被当做连续序列的一部分计算一次, 这对于保证 O(n) 的时间复杂度至关重要。
-
计算序列长度:
-
如果
num
是一个序列的起始数字,那么我们就开始沿着序列一直向下查找:currentNum = num;
: 将当前数字设置为该起始数字。currentStreak = 1;
: 初始化当前序列的长度为 1。while (numSet.has(currentNum + 1))
: 只要currentNum + 1
存在于numSet
中,就说明序列还在继续。currentNum += 1;
: 向下移动到序列中的下一个数字。currentStreak += 1;
: 增加序列的长度。
-
-
更新最长序列长度:
longestStreak = Math.max(longestStreak, currentStreak);
: 在每次找到一个序列后,将当前序列的长度与longestStreak
进行比较,并更新longestStreak
。
-
返回结果:
- 最后,返回
longestStreak
,即最长连续序列的长度。
- 最后,返回
实列与展示



四、结语
再见!