LeetCode128:哈希集合巧解最长连续序列

LeetCode128:哈希集合巧解最长连续序列

一、题目回顾

LeetCode 128 题「最长连续序列」是一道中等难度的数组题,核心要求如下:给定一个未排序的整数数组 nums,找出其中数字连续的最长序列(不要求序列元素在原数组中连续)的长度,且必须设计时间复杂度为 O (n) 的算法。

示例直观理解:

  • 输入 nums = [100,4,200,1,3,2],输出 4(最长序列是 [1,2,3,4]);
  • 输入 nums = [0,3,7,2,5,8,4,6,0,1],输出 9(完整连续序列 0-8)。

二、解法思路:哈希集合 + 起点判定

题目要求 O (n) 时间,因此不能用排序(排序时间 O (nlogn)),需要借助「哈希集合」的快速查找特性(查找操作 O (1))。

核心思路是:

  1. 将数组元素存入哈希集合,实现 “是否存在某数” 的快速判断;
  2. 遍历集合中的每个数 x仅当 x-1 不在集合中时,才将 x 作为 “连续序列的起点”;
  3. 从起点 x 开始,依次查找 x+1、x+2... 是否在集合中,统计该序列的长度;
  4. 维护一个全局变量,记录所有序列的最长长度。

这个思路的巧妙之处在于:非起点的数会被跳过,每个数只会被遍历一次,从而保证整体时间复杂度为 O (n)。

三、C++ 代码解析

先看完整代码,再逐行拆解:

cpp

运行

class Solution { public: int longestConsecutive(vector<int>& nums) { // 1. 将数组元素存入哈希集合(去重+快速查找) unordered_set<int> hash(nums.begin(), nums.end()); int len = 0; // 记录最长序列长度 // 2. 遍历集合中的每个数 for (int x : hash) { // 3. 仅当x-1不存在时,x才是序列起点(避免重复计算) if (hash.count(x - 1)) { continue; } // 4. 从起点x开始,扩展连续序列 int y = x + 1; while (hash.count(y)) { ++y; } // 5. 更新最长长度(y-x是当前序列的长度) len = max(len, y - x); } return len; } }; 

代码关键细节

  1. 哈希集合的作用
    • 用 unordered_set 存储数组元素,既实现了去重(重复元素不影响连续序列长度),又能在 O (1) 时间内判断某数是否存在。
  2. 起点判定逻辑
    • if (hash.count(x - 1)) continue;:如果 x-1 存在,说明 x 不是当前连续序列的起点(比如 x=2 时,若 x-1=1 存在,则 2 属于以 1 为起点的序列),直接跳过,避免重复遍历。
  3. 序列长度计算
    • 从起点 x 出发,用 y 不断向后扩展(y++),直到 y 不在集合中,此时 y - x 就是当前连续序列的长度。

四、复杂度分析

  • 时间复杂度:O(n)
    • 存入集合的时间是 O (n);
    • 遍历集合时,每个元素最多被访问 1 次(只有起点会触发后续的扩展循环,非起点会被跳过),因此整体遍历时间是 O (n)。
  • 空间复杂度:O(n)
    • 哈希集合存储了数组的所有元素,空间开销与数组长度成正比。

五、解法小结

这个解法的核心是通过 “起点判定” 避免重复计算,结合哈希集合的快速查找,既满足了 O (n) 的时间要求,又保证了逻辑的简洁性。

相比暴力解法(枚举每个数后遍历后续元素,时间 O (n²)),这个方法用空间换时间,是这道题的最优解之一。

Read more

c++新手 使用trae 搭建c++开发环境,提示不支持cppdbg

Trae IDE搭建C++开发环境完全指南:从0到1的实战经验分享 **【补充: 今天偶然翻到一篇文章,就在我解决Trae无法调试C++的问题之后,我发现的。贴出来,告诉大家为什么无法在trae中调试C++。 下面两篇文章的大致是说微软不让第三方使用官方开发的C++插件。 微软开始发力了,Trae用不了最新版的c++插件了 (https://zhuanlan.zhihu.com/p/1907744061080733167) trae插件安装官方文档 https://docs.trae.cn/ide/manage-extensions trae 的文档也侧面证实了这个消息,并以及给出了解决方案。】** 🔥 前言:从Java到C++的转型之旅 大家好,我是一名从Java转型C++的全栈开发者。最近尝试使用Trae IDE(基于VSCode开发的智能编程工具)搭建C++开发环境时,遇到了不少"坑"——从插件安装失败、配置文件报错到依赖库编译错误,踩了很多坑。

By Ne0inhk
【Spring Boot 报错已解决】深度解析 java.lang.NullPointerException:UserService 为 null 的解决方法与避坑指南

【Spring Boot 报错已解决】深度解析 java.lang.NullPointerException:UserService 为 null 的解决方法与避坑指南

文章目录 * 引言 * 一、问题描述 * 1.1 报错示例 * 1.2 报错分析 * 1.3 解决思路 * 二、解决方法 * 2.1 方法一:使用@Autowired注解注入依赖 * 2.2 方法二:使用构造方法注入依赖 * 2.3 方法三:使用setter方法注入依赖 * 2.4 方法四:检查组件扫描和注解配置 * 三、其他解决方法 * 四、总结 引言 在Spring Boot开发过程中,NullPointerException是开发者经常遇到的错误之一,而“Cannot invoke “com.xxx.service.UserService.add()” because “this.

By Ne0inhk
Java重入锁(ReentrantLock)全面解析:从入门到源码深度剖析

Java重入锁(ReentrantLock)全面解析:从入门到源码深度剖析

文章目录 * 引言 * 第一部分:重入锁基础概念 * 1.1 什么是重入锁? * 1.2 为什么需要重入锁? * 1.3 ReentrantLock的基本用法 * 第二部分:ReentrantLock的核心特性 * 2.1 可重入性 * 2.2 公平锁与非公平锁 * 2.2.1 概念解析 * 2.2.2 为什么默认非公平锁? * 2.2.3 源码层面的差异 * 2.3 可中断锁 * 2.4 限时等待锁 * 2.5 条件变量(Condition) * 第三部分:ReentrantLock与synchronized的全面对比 * 3.1 异同点总结 * 3.2

By Ne0inhk

【JavaScript】React 实现 Vue 的 watch 和 computed 详解

React 实现 Vue 的 watch 和 computed 详解 文章目录 * React 实现 Vue 的 watch 和 computed 详解 * 二、实现 Vue 的 computed(计算属性) * 方式一:基础版(函数组件 + useMemo,推荐) * 完整代码示例 * 核心解释 * 方式二:简化版(仅简单计算,无需缓存) * 简短代码示例 * 说明 * 三、实现 Vue 的 watch(监听数据变化) * 场景一:基础监听(函数组件 + useEffect) * 完整代码示例 * 核心解释 * 场景二:深度监听(

By Ne0inhk