哈希表实现O(n)最长连续序列算法详解
1. 问题理解与解法概述给定一个未排序的整数数组我们需要找出数字连续的最长序列的长度。这里的连续指的是数值上的连续如1,2,3,4而不是在原数组中的位置连续。题目要求我们设计一个时间复杂度为O(n)的算法。1.1 问题示例解析以示例1为例输入[100,4,200,1,3,2]最长连续序列是[1,2,3,4]长度为4这个例子中虽然数字在原数组中不是连续的但数值上是连续的。我们需要找到这样的序列中最长的一个。1.2 常见解法分析最直观的解法是先对数组进行排序然后遍历查找最长连续序列但排序的时间复杂度是O(nlogn)不满足题目要求的O(n)。因此我们需要寻找更高效的解法。2. 哈希表解法详解2.1 算法思路我们可以利用哈希集合HashSet来实现O(n)时间复杂度的解法具体思路如下将所有数字存入哈希集合去重遍历哈希集合中的每个数字对于每个数字检查它是否是某个连续序列的起点即num-1不在集合中如果是起点则向后延伸计算序列长度记录遇到的最大长度2.2 代码实现解析class Solution { public: int longestConsecutive(vectorint nums) { unordered_setint num_set; // 将数字存入哈希集合自动去重 for(const int num : nums) { num_set.insert(num); } int longestStreak 0; // 遍历哈希集合中的每个数字 for(const int num : num_set) { // 检查是否是序列起点 if(!num_set.count(num-1)) { int currentNum num; int currentStreak 1; // 向后延伸计算序列长度 while(num_set.count(currentNum1)) { currentNum 1; currentStreak 1; } // 更新最大长度 longestStreak max(longestStreak, currentStreak); } } return longestStreak; } };2.3 时间复杂度分析虽然代码中有嵌套循环但每个数字最多被访问两次一次在外层循环一次在内层while循环因此总时间复杂度是O(n)。3. 算法优化与细节讨论3.1 为什么检查num-1我们只对可能是序列起点的数字进行处理即num-1不在集合中的数字。这样可以避免重复计算如果一个数字不是序列起点即num-1存在那么它一定会在某个序列的中间或末尾被处理到这样确保每个序列只被处理一次3.2 边界情况处理需要考虑的特殊情况包括空数组直接返回0所有数字相同如[1,1,1]最长序列长度为1负数和大数算法对数值范围没有特殊要求3.3 空间复杂度使用哈希集合存储所有数字空间复杂度是O(n)。4. 其他解法对比4.1 排序解法虽然排序解法简单直观sort(nums.begin(), nums.end()); // 然后遍历查找最长序列但时间复杂度为O(nlogn)不满足题目要求。4.2 并查集解法可以使用并查集数据结构将每个数字视为一个独立集合对于每个数字如果num1存在则合并这两个集合最后找出最大的集合这种方法时间复杂度也是O(n)但实现起来更复杂。5. 实际应用与变种问题5.1 实际应用场景这种算法可以应用于数据库中的连续记录查询日志分析中的连续事件检测游戏中的连续成就统计5.2 变种问题找出所有最长连续序列允许有k个数字缺失的最长序列二维矩阵中的最长连续序列6. 常见错误与调试技巧6.1 常见错误忘记处理空数组的情况没有去重导致错误计数错误计算序列长度如没有重置currentStreak6.2 调试技巧使用小测试用例手动验证打印中间变量如currentNum和currentStreak检查边界条件如最小/最大整数7. 性能优化建议对于C使用unordered_set而不是set哈希表比红黑树更快提前检查nums.size()0的情况考虑内存局部性可以尝试先分配足够空间8. 不同语言实现要点8.1 Python实现def longestConsecutive(nums): num_set set(nums) longest 0 for num in num_set: if num - 1 not in num_set: current num current_len 1 while current 1 in num_set: current 1 current_len 1 longest max(longest, current_len) return longest8.2 Java实现class Solution { public int longestConsecutive(int[] nums) { SetInteger num_set new HashSet(); for (int num : nums) { num_set.add(num); } int longest 0; for (int num : num_set) { if (!num_set.contains(num-1)) { int current num; int current_len 1; while (num_set.contains(current1)) { current 1; current_len 1; } longest Math.max(longest, current_len); } } return longest; } }9. 算法证明与正确性9.1 算法正确性证明完整性算法会检查所有可能的序列起点无重复每个序列只被处理一次无遗漏所有数字都被考虑在内9.2 数学归纳法基础情况空数组或单元素数组显然正确 归纳步骤假设对n-1个元素正确加入第n个元素时如果它连接了两个序列会正确合并如果它是新序列起点会被正确识别如果它在序列中间会被跳过10. 扩展思考与练习题10.1 相关练习题Leetcode 674. 最长连续递增序列Leetcode 673. 最长递增子序列的个数Leetcode 300. 最长递增子序列10.2 挑战问题如何修改算法使其在O(n)时间内同时返回所有最长连续序列提示可以在遍历时记录所有序列最后筛选出最长的。