LC100_128
题目大意: 给定一个未排序的整数数组 nums,找出其中数字连续的最长序列的长度。
例如: [100, 4, 200, 1, 3, 2],最长连续序列是 [1, 2, 3, 4],长度为 4
Key constraints:
- Time complexity: This is the greatest challenge, which means we cannot use the O(NlogN) solution based on sorting
排序再查找,时间复杂度是 O(NlogN),不符合要求。要达到 O(N),通常会想到哈希表 (Hash Table),因为它提供了平均 O(1) 的查找和插入
核心思想:用哈希表存储所有数字,并只从“连续序列的起点”开始探索
First Step: Save all numbers into a hash set
第一步:将所有数字存入哈希集合 (Set):
- 快速地判断一个数字是否存在于数组中。std::unordered_set 在 C++ 中提供平均 O(1) 的查找效率
- 将
nums数组中的所有元素都插入到std::unordered_set<int>中。这步操作的时间复杂度是 O(N)
第二步:遍历数组,寻找连续序列的起点
- 遍历原始数组
nums中的每一个数字num - 对于每个 num,我们想判断它是不是一个连续序列的起点
- 如何判断起点? 如果
num - 1不在哈希集合中,那么num就是一个潜在的连续序列的起点
第三步:从起点开始向后探索
- 确定
num是一个起点,那么就以num为起点,开始向后查找连续的数字 - 维护一个
current_num = num和current_streak = 1 - 不断检查
current_num + 1是否在哈希集合中:- 如果在,说明序列还在继续,current_num 增加 1,current_streak 增加 1
- 如果不在,说明序列断了,当前 current_streak 就是以 num 为起点的最长连续序列长度
- 用一个 max_streak 变量来记录迄今为止找到的最长的连续序列长度,每次探索完一个序列后更新 max_streak = std::max(max_streak, current_streak)
code
class Solution {
public:
int longestConsecutive(std::vector<int>& nums) {
// 如果数组为空,直接返回0
if (nums.empty()) {
return 0;
}
// 1. 将所有数字存入哈希集合,方便 O(1) 查找
std::unordered_set<int> num_set(nums.begin(), nums.end());
int max_streak = 0; // 记录找到的最长连续序列长度
// 2. 遍历原始数组中的每个数字
for (int num : nums) {
// 3. 判断当前数字是否是连续序列的起点
// 如果 num - 1 不在集合中,说明 num 是一个新序列的起点
if (num_set.find(num - 1) == num_set.end()) {
// 是起点,开始向后探索这个序列
int current_num = num;
int current_streak = 1;
// 检查 current_num + 1 是否存在,直到序列中断
while (num_set.count(current_num + 1)) {
current_num++;
current_streak++;
}
// 更新最长序列长度
max_streak = std::max(max_streak, current_streak);
}
}
return max_streak;
}
};
复杂度分析:
- 时间复杂度:O(N)
- 将
N个元素插入unordered_set:平均 O(N) - 遍历
N个元素:O(N) - 在内部的
while循环中,每个数字x(num_set.count(x + 1)) 只会在其作为某个连续序列的一部分时被访问一次。虽然有嵌套循环,但总的num_set.count()调用次数以及current_num++的次数,加起来不会超过N次。因此,总体时间复杂度是线性的
- 将
- 空间复杂度:O(N)
unordered_set最多会存储N个数字