- 最长连续序列
一、题目
给定一个未排序的整数数组 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 <= 10(5)
- -10(9) <= nums[i] <= 10(9)
二、思路
因为本题有时间复杂度O(n)的要求,所以不能排序(排序的时间复杂度至少为 O(n log n)
于是通过「哈希集合」实现快速查找,并通过「剪枝」避免重复遍历。
核心思路:
是对于nums中的元素x,先判断x-1是否在数组中,来找到起点,以此时的x为起点,判断x+1,x+2 ...是否在数组中。
关键观察:
一个连续序列的「起点」满足:x-1 不存在于数组中(比如序列 [1,2,3,4] 的起点是 1,因为 0 不在数组中)。
- 若 x-1 存在,则 x 不是起点,无需遍历以 x 为起点的连续序列(避免重复计算);
- 若 x-1 不存在,则 x 是起点,需遍历 x+1、x+2... 直到不存在,统计序列长度。
为了满足时间复杂度的要求,一定要做的两个优化 :
1)使用set集合(哈希集合 ),可以以O(1)的时间复杂度来判断数字是否在nums中。
2)找到最开始的x起点,这样其他的点将不进入内部循环
还可以进行剪枝优化 :
如果现在得到的最长连续序列的长度已经超过了数组长度的一半,则可以break了,因为假如一共10个数,现在的最长已经是6,则后面的总共才4个,就算这4个全部连续,也没有6大,所以没有必要再进行运算。
三、代码
javascript
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function(nums) {
//转为set去重,实现O(1)查找,同时处理空数组/重复元素场景。
const st = new Set(nums);
let ans = 0;
//遍历去重后的每个元素
for(const x of st){
//若x-1存在,则x不是起点,跳过,避免重复遍历,增加时间复杂度
if(st.has(x-1)){
continue;
}
//此时x是起点,统计连续序列长度
let y = x+1;
//使用while遍历,直到连续序列中断
while(st.has(y)){
y++;
}
//在不连续的数组中和往期的ans打擂台,如[1,2,3,4]和[100,101]
ans=Math.max(ans,y-x);
//剪枝优化:当前最长长度已经超过去重后数组长度的一半,剩下的就算全部连续也不可能比现在的最长序列长度长,无需继续遍历
if(ans*2>=st.size){
break;
}
}
return ans;
};
四、复杂度
时间复杂度:O(n) ,其中 n 是 nums 的长度。在二重循环中,每个元素至多遍历两次:在外层循环中遍历一次,在内层循环中遍历一次(相加为2n)。所以二重循环的时间复杂度是 O(n) 的。比如 nums=[1,2,3,4],其中 2,3,4 不会进入内层循环,只有 1 会进入内层循环。所以它们不是相乘的关系,而是相加的关系
空间复杂度:O(m)。其中 m 是 nums 中的不同元素个数。