“最长连续子序列”分两类:数值连续(如{1,2,3,4})用unordered_set去重+找起点o(n)求解;下标连续即最长连续递增子数组,线性扫描o(n)解决。二者不可混淆。

什么是“最长连续子序列”?先确认问题定义
很多人一看到“最长连续子序列”就默认是 std::sort 后扫一遍,但实际要分清楚:你指的是数值上连续(如 3,4,5,6),还是下标上连续(即子数组)?C++ 标准库没有现成函数解决前者,后者就是“最长连续递增子数组”,两者解法完全不同。
如果你要的是数值连续(允许乱序输入,比如 {100, 4, 200, 1, 3, 2} → 最长数值连续段是 {1,2,3,4},长度 4),核心思路是去重 + 哈希查找起点;如果是下标连续({1,3,2,4,5} 中最长递增连续段是 {2,4,5}?不,其实是 {4,5},因为下标必须相邻),那就直接线性扫描。
用 unordered_set 找数值连续的最长段(O(n) 解法)
关键在于避免对每个数都往左右扩展——那样最坏 O(n²)。正确做法是:只从某个数的“左邻居不存在”时才开始向右计数,保证每个数最多被访问两次。
- 先把所有数插入
std::unordered_set<int></int>去重 - 遍历集合,对每个
num,检查num - 1是否存在;不存在,说明num是某段起点 - 从
num开始,用 while 循环查num + 1、num + 2… 直到断开,记录最大长度 - 注意:不能用
vector或array直接遍历原数组,必须用unordered_set,否则重复元素会干扰起点判断
示例片段:
std::vector<int> nums = {100, 4, 200, 1, 3, 2};
std::unordered_set<int> s(nums.begin(), nums.end());
int max_len = 0;
for (int num : s) {
if (s.find(num - 1) == s.end()) { // 确实是起点
int curr = num, curr_len = 1;
while (s.find(curr + 1) != s.end()) {
curr++;
curr_len++;
}
max_len = std::max(max_len, curr_len);
}
}
// max_len == 4
</int></int>
找下标连续的最长递增子数组(简单线性扫描)
这才是真正的“子数组”问题:要求索引连续,且值严格递增。不需要哈希,一次遍历搞定,空间 O(1)。
- 维护当前长度
curr_len和最大长度max_len - 从 i=1 开始,若
nums[i] > nums[i-1],则curr_len++;否则重置curr_len = 1 - 每次更新
max_len = std::max(max_len, curr_len) - 注意边界:空数组返回 0,单元素返回 1
- 如果题目允许非严格递增(≥),改比较符即可,但语义已不同
常见错误:把 curr_len 初始化为 0,导致单元素数组结果为 0;或忘记在循环外再取一次 max_len —— 其实不用,因为每次更新都在循环内做。
为什么不能直接用 sort + 遍历?
可以,但破坏了原始结构,且时间复杂度升到 O(n log n),还丢失了重复元素的处理逻辑。更隐蔽的问题是:如果输入含重复值(如 {1,2,2,3}),单纯排序后扫会误判连续段长度为 4(实际数值连续段仍是 {1,2,3},长度 3)。
所以除非明确要求“排序后最长连续”,否则别碰 std::sort。另外,std::set 虽自动去重+有序,但插入已是 O(n log n),不如 unordered_set + 手动找起点高效。
真正容易被忽略的点是:数值连续 ≠ 下标连续,而 C++ 里没有任何标准容器或算法能自动区分这两者——你得先用语言把问题说清楚,再选数据结构,而不是反过来。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











