闲来无事,写一道算法题,竟有这么大收获。
128. 最长连续序列
题目难度: 中等
给定一个未排序的整数数组 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
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
解题过程
解题思路一:哈希表
我们都知道,哈希表通过键(key)来访问记录值(value),易知key存数字,那么value存什么呢?
value存自己处在的连续序列的长度。
- 遍历nums数组
- 重复的数字跳过
- 获取当前数字(key)的左邻居所在序列的长度(value)
- 获取当前数字(key)的右邻居所在序列的长度(value)
- 将新序列的长度存入自己的value里
- 更新最长连续序列值
- 更新新序列的左端数字的value
- 更新新序列的右端数字的value
解题代码
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function(nums) {
let max = 0;
let map = new Map()
for(let i = 0; i < nums.length; i++){
if (!map.has(nums[i])) {
let left = map.get(nums[i] - 1) || 0
let right = map.get(nums[i] + 1) || 0
let cur = left + right + 1
map.set(nums[i], cur)
max = Math.max(max, cur)
map.set(nums[i] - left, cur)
map.set(nums[i] + right, cur)
}
}
return max;
};
在介绍第二种方法之前,我们先认识第二种方法要用到的Set
数据结构。
Set介绍
在JavaScript中,Set
是ES6引入的一种新的数据结构,它类似于数组,但是成员的值都是唯一的,没有重复的值。Set
对象用来存储唯一的值的集合,这些值可以是任何类型的,包括对象和原始类型。
以下是一些关于 Set
的关键点:
- 创建
Set
: 可以通过new Set()
来创建一个新的Set
对象,也可以传入一个可迭代的对象(如数组)来初始化Set
。 - 添加元素: 使用
add()
方法来向Set
添加元素。 - 删除元素: 使用
delete()
方法从Set
中移除元素。 - 检查元素: 使用
has()
方法来检查Set
是否包含某个元素。 - 获取大小:
Set
提供了size
属性来返回当前Set
中的元素数量。 - 迭代
Set
: 可以使用for...of
循环或forEach()
方法来迭代Set
中的元素。 - 转换为数组: 可以使用扩展运算符
...
或Array.from()
来将Set
转换为数组。
// 创建一个空的 Set
let s = new Set();
// 向 Set 中添加元素
s.add(1);
s.add(2);
s.add(3);
s.add(2); // 这个 2 不会被添加,因为已经存在
// 检查 Set 是否包含某个元素
console.log(s.has(2)); // true
console.log(s.has(4)); // false
// 从 Set 中删除元素
s.delete(2);
// 获取 Set 的大小
console.log(s.size); // 2
// 迭代 Set
s.forEach(value => console.log(value));
// 将 Set 转换为数组
let arr = [...s];
console.log(arr); // [1, 3]
解题思路二:Set
由于查找 Set
中的元素的时间复杂度是 O(1),所以这次我们使用不同于解题思路一的解法:
- 确定是否为左起点,
- 若非,则跳过;
- 若是,则不断遍历当前数字的右一位,直到
set
里不存在右一位,说明该序列已排完。
换言之:
- 查找
Set
中的元素的时间复杂度是 O(1),JS 的 Set 能给数组去掉重复元素 - 将数组元素存入
set
中,遍历数组 nums - 如果 当前项 - 1 存在于
set
,说明当前项不是连续序列的起点,跳过,继续遍历 - 当前项没有“左邻居”,它就是连续序列的起点
- 不断在
set
中查看 cur + 1 是否存在,存在,则 count +1 cur
不再有 “右邻居” 了,就算出了一段连续序列的长度
解题代码
var longestConsecutive = (nums) => {
const set = new Set(nums) // set存放数组的全部数字
let max = 0
for (let i = 0; i < nums.length; i++) {
if (!set.has(nums[i] - 1)) { // nums[i]没有左邻居,是序列的起点
let cur = nums[i]
let count = 1
while (set.has(cur + 1)) { // cur有右邻居cur+1
cur++ // 更新cur
count++
}
max = Math.max(max, count) // cur不再有右邻居,检查count是否最大
}
}
return max
}
亦可使用Set去除重复元素,再模仿解题思路一的做法做完剩下流程。
如有错误之处,敬请大佬留言改正。