当前位置: 首页 > news >正文

力扣hot100之最长连续序列(java版)

1、题目描述

给定一个未排序的整数数组 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

2、思路

第一步:理解题意 —— 什么是“连续序列”?

举个例子:

输入:[100, 4, 200, 1, 3, 2]

输出:4

解释:最长连续序列是 [1, 2, 3, 4],长度为 4。

注意:

  • 不要求原数组中位置连续,只要数值连续。

  • 序列必须是严格递增 +1 的整数序列。

  • 数组可能有重复元素(如 [1,2,2,3] → 最长是 [1,2,3],长度3)。

 知识点1:理解“连续序列”的定义

连续序列 = 一组整数,彼此相差1,如 3,4,5,6 —— 这是“公差为1的等差数列”。


第二步:暴力解法是什么?

暴力思路:

  1. 对数组排序 → Arrays.sort(nums) → O(n log n)

  2. 遍历排序后的数组,计算连续递增+1的长度 → O(n)

  3. 记录最大长度。

 这种方式可以做出来,但时间复杂度是 O(n log n),不符合题目要求的 O(n)。

 知识点2:排序的时间复杂度下限

比较排序(如快排、归并)最坏/平均都是 O(n log n)。题目要求 O(n),意味着不能排序!


第三步:如何在 O(n) 时间内解决?→ 引入哈希集合(HashSet)

核心思想:

我们不想排序,但又想“快速查找某个数字是否存在”,怎么办?

HashSet!它支持 O(1) 平均时间 的插入和查找!

知识点3:哈希表(HashSet)的基本原理
  • HashSet 是基于哈希表实现的无序、不重复集合。它是一个单列集合,一次存取一个元素,存入和取出的顺序不一致(不是随机的,是有特定的规则)。

  • 插入、删除、查找 平均时间复杂度是 O(1)。

  • 底层是数组 + 链表/红黑树(Java 8+优化)。

  • 适用于“快速判断元素是否存在”的场景。

 第一步操作:把所有数字放进 HashSet

//这里所有的数都是整数,因此泛型限制为整型 
Set<Integer> numSet = new HashSet<>();//集合的通用遍历方式--增强for循环for (int num : nums) {numSet.add(num);//hashSet中添加元素的方法}

现在,我们可以用 numSet.contains(x) 在 O(1) 时间内判断 x 是否存在!

第四步:如何避免重复计算?---->只从“序列起点”开始!

 关键洞察:

如果我们对每个数字都尝试向后扩展(比如对 2 扩展出 3,4,5…),那 1,2,3,4 会被重复计算多次!

 我们只应该从“连续序列的起点”开始扩展!

 什么是“起点”?

一个数 x 是起点,当且仅当 x - 1 不存在于集合中

比如:

  • 对于 1:0 不存在 → 1 是起点 

  • 对于 2:1 存在 → 2 不是起点 

  • 对于 100:99 不存在 → 100 是起点 

这样,我们就能保证每个连续序列只被计算一次!

 知识点4:避免重复计算的思维技巧 —— “只处理代表元”

在很多算法题中(如并查集、图遍历),我们通过“只处理某个特定代表”来避免重复工作。这里是“只处理序列起点”。


 第五步:对每个起点,向后扩展,计算长度

 算法步骤:

对每个数 num

  1. 如果 num - 1 不在集合中 → 它是起点。

  2. num 开始,不断检查 num + 1, num + 2, ... 是否在集合中。

  3. 记录当前序列长度。

  4. 更新全局最大长度。

for (int num : numSet) {//用 numSet.contains(x) 在 O(1) 时间内判断 x 是否存在if (!numSet.contains(num - 1)) { // 是起点int currentNum = num;int currentStreak = 1;​        //用 numSet.contains(x) 在 O(1) 时间内判断 x 是否存在while (numSet.contains(currentNum + 1)) {currentNum++;currentStreak++;}​        //更新最大连续序列的长度longestStreak = Math.max(longestStreak, currentStreak);}}
 知识点5:贪心扩展 + 局部最优 → 全局最优

我们“贪心”地从每个起点尽可能向右扩展,局部求出以它为起点的最长序列,然后取全局最大值。

 第六步:为什么时间复杂度是 O(n)?

看起来有两层循环,怎么会是 O(n)?

外层循环遍历所有数字 → O(n)

内层 while 循环看起来会重复访问数字 → 但实际上每个数字最多被访问两次:

  • 一次在 for 循环中判断是否是起点。

  • 一次在某个起点的 while 循环中被访问(作为序列中间元素)。

 总访问次数 <= 2n → 时间复杂度 O(n)

知识点6:摊还分析(Amortized Analysis)

虽然某次操作可能耗时较长,但平均到所有操作上,每次操作的“均摊成本”很低。这里是“每个元素最多被访问两次”,所以总复杂度是线性的。


第七步:边界情况处理

//当这个赎罪是空的或者它的成都为0都应该立刻停止运行,直接返回0 
if (nums == null || nums.length == 0) {return 0;}
知识点7:防御性编程 & 边界条件

任何算法题都要考虑:

  • 空数组

  • null 输入

  • 单元素

  • 全重复元素

  • 负数、零、极大极小值


 完整思路总结

  1. 明确目标:找最长的“数值连续”序列,不要求位置连续。

  2. 拒绝排序:因为 O(n log n) 不符合要求。

  3. 引入 HashSet:用空间换时间,实现 O(1) 查找。

  4. 聪明遍历:只从“起点”(x-1 不存在)开始扩展,避免重复。

  5. 贪心扩展:对每个起点,向右一直找 x+1, x+2... 直到断掉。

  6. 记录最大值:每次扩展完,更新最长长度。

  7. 复杂度分析:每个元素最多访问两次 → O(n)。

  8. 边界处理:空数组返回0。

3、完整代码实现

方法一:暴力破解(排序+相邻比较)

暴力破解虽然可以解决,但是不符合题意,但是我们可以先用暴力破解来解决此题,然后再想想可不可以把时间复杂度优化。

class Solution{public int longestConsecutive(int[] nums){if(nums == null || nums.length == 0){return 0;}//先对数组进行排序,使用Arrays自带的排序方法Arrays.sort(nums);int maxCount = 0;//记录最长连续序列的长度int currentCount = 1;//当前连续序列的长度//这里需要注意一下:数组的长度表示是nums.length,length表示的是属性//字符串的长度表示是chars.length(),这里表示使用这个length()方法来获取字符串的长度for(int i = 1;i < nums.length;i++){if(nums[i] == nums[i-1]){//如果当前元素和前一个元素相同,则跳过continue;//跳过本次循环,进行下一次循环}else if(nums[i] == nums[i-1] + 1){//如果当前元素是前面一个元素的连续值,则增加当前连续序列的长度currentCount++;}else{//如果当前元素不是前一个元素的连续值,则重置当前连续序列的长度maxCount = Math.max(maxCount,currentCount);currentCount = 1;}}//最后一次更新最大长度maxCount = Math.max(maxCount,currentCount);return maxCount;}
}

问题:为什么要从1开始遍历?
因为你要比较 nums[i] 和 nums[i-1],也就是“当前元素”和“前一个元素”。
如果 i = 0,那你就要访问 nums[-1] → 数组越界 ❌
所以必须从 i = 1 开始,这样 i-1 = 0 是合法索引 ✅

方法二:使用哈希表hashSet

class Solution{public int longestConsecutive(int[] nums){//0、先判断边界情况if(nums == null || nums.length == 0){return 0;}//1、把所有的数字都存入到HashSet中,去重+快速查找Set<Integer> numSet = new HashSet<>();for(int num : nums){numSet.add(num);}int longestStreak = 0;//记录最长连续序列的长度//2、迭代器遍历:也就是增强for循环;遍历每个数字,只从‘起点’开始扩展for(int num : numSet){//如果num-1不存在,说明num就是某个连续序列的起点if(!numSet.contains(num -1)){int currentNum = num;//当前正在检查的数字int currentStreak = 1;//当前连续序列的长度//3、从起点开始扩展,向右扩展//这里的循环条件是判断“起点”后的扩展在HashSet中是否存在while(numSet.contains(currentNum + 1)){currentNum++;//每遍历一次就+1,在HashSet中查询有没有连续的序列currentStreak++;//如果有连续的序列就继续寻找,且长度+1}//4、更新全局最大长度longestStreak = Math.max(longestStreak,currentStreak);}}return longestStreak;}
}
http://www.dtcms.com/a/546152.html

相关文章:

  • 买了两台服务器可以做网站吗seoapp推广
  • GXDE For deepin 25:deepin25 能用上 GXDE 了!
  • 搜房网站要怎么 做网站做成app
  • 网站建设教学视频百度云盘无锡电商网站设计
  • 【MIT 6.5840/6.824】Lab3 Raft
  • 在哪网站开发软件制作企业网站得多长时间
  • 异构系统集成提速:重构企业数据流转架构
  • 在OpenHarmony上适配图形显示【5】——DRM 设备信息查询工具drm_info
  • 校园网站制度建设淘宝网页版怎么退出登录
  • Rust 中的零拷贝技术:从原理到实践的深度探索 [特殊字符]
  • 泰州网站建设多少钱什么是4c品牌建设模型
  • 公司网站模板源代码亳州网站建设推广
  • Linux驱动的加载与卸载
  • 二学一做专题网站无锡网页制作报价
  • 装修公司做自己网站珠海网站制作费用
  • 手机网站建站公司wordpress关于本站
  • 网站数字签名做网站能挣钱么
  • 温州做网站的wordpress the7.6
  • 中华古文明的视觉史诗:郭泰来以当代彩墨重构“上古三经”——迟来的祝贺:图书《山海经》荣获“出版奖”
  • Rust BTreeMap 红黑树
  • 为代理网站做网站wordpress站群作用
  • 网站上社保做增员怎么做wordpress html 代码
  • Lua--协程
  • 建立网站目录结构的原则优化二十条
  • 远近互联网站建设成品网站源码1688danji6
  • 枣庄三合一网站开发公司软件开发模型的v模型图
  • 生成式人工智能在教育领域的技术适配性研究:挑战、风险与应对方案
  • 技术解析:AI出海两极分化下的破局指南
  • CAN总线网关到底是什么:双5g车载网关案例
  • GifCam,一款小巧的GIF录制工具