【LeetCode】128. 最长连续序列——哈希的应用(3)

文章目录

  • 1、思路
  • 2、解题方法
  • 3、复杂度
    • 时间复杂度:
    • 空间复杂度:
  • 4、Code

Problem: 128. 最长连续序列

在这里插入图片描述

1、思路

我会用一种做题者的思路来去看待这道题。

我们在乍一看到这道题的时候,看到它的时间复杂度要求为O(N),然后又要求去找序列(就是让你判断这个数的前面一个数在不在这个数组里,这个数的后面一个数在不在数组里)。按照我们平时暴力的做法(也是最先想到的做法),遍历一个O(N),然后判断每个元素在不在又是O(N),然后有可能会有N个元素,这样N* N *N最坏的情况就都有可能是O(N^3)了。要我们找序列,又要我们O(N),这不扯淡么。

因为所谓O(N)的复杂度,就是要让你一遍过,或者你能说出来几遍过(也即常数个O(N),即O(kN),k为常数),所以,我们说要你说常数遍过就有些扯淡了。

这个时候,我们就可以想到用时间换空间的思想。 并且,用哈希表的存储这样一个空间,来换遍历查找的时间,是一个非常暴力且不要脸的做法(但是并不妨碍我说这种做法好哈哈哈哈)。说它不要脸,是因为它简单易行。

什么意思?简单举个例子:假如有个数组,你要找比某个元素大1(或者小1)的元素在不在该数组里面,按照最粗暴的方法,那么你需要遍历整个数组,也就是O(N)的时间复杂度。但是如果你用哈希的话,那就变成O(1)啦

那么,现在对于数组内的每一个元素,我都要去找比它小1的元素在不在该数组里,如果按照原来的方法,我需要O(N*2)的时间复杂度。但是如果我用了哈希表,我就一下子变成了O(N)了。

没用什么多难的思想(即没有怎么多人为的操作,参见DP的某些题目),但是就轻轻松松完成了复杂度由时间向空间的转化。所以说,它很不要脸哈哈哈哈哈。

我们就是用这样一个核心的思想来去做这道题。

2、解题方法

在一开始拿到这道题,并且明确了哈希表有这样一个功能之后,最先想到的,是用unordered_map这样一个结构。因为不是要O(N)嘛,我是用哈希来去降了一次,但是我似乎遍历一遍数组需要O(N),但还需要去递归地去找 比每个元素小1的、小2的、小3的…小n的 在不在里面,这不是还是有个O(N)吗,然后合在一块不还是O(N^2)?

然后我想着,前面的第一次O(N)是不可避免的(因为你总要遍历整个数组嘛)。但是后面的那个O(N),可不可以用unordered_map里的 <key, value>来去标记我每个元素有没有遍历过呢?如果说,我在某次找比某个元素小1的、小2的…小n的时候遍历过了,那么我在第一个遍历数组的那个O(N)循环里就不需要再去遍历它了,但是这就有可能造成新的问题,就是我怎么知道谁是最长的呢?比如对于样例:

[100, 4, 200, 1, 3, 2, 5]

如果在外层第一层循环遍历到4的时候,我依靠哈希在内层找到了后面的1,2,3,并且标记它们已经被遍历过。那后面第一层循环再遍历到数组1,2,3的时候就直接跳过。但是如果我后面的5来了,我该怎么办呢?而且我这个时候4已经遍历过了。

如果大家能够明白我上面想表达的意思了,那么大家可以先不着急往下看,自己想一想该如何做。

其实做法有很多啦。在这里呢,就说两种。

1.是采用计数的方法。我们刚刚不是用了unordered_map嘛,那就可以在value中来去存储长度的相关信息。这样,在5来了之后,将它现在已有的长度加上4中的value中存储的长度,作为5的长度来使用。

2.这个做法就更加简单粗暴了就是如果这个数不是边界,就别理它,只有当它是边界的时候我们再去给它计数。什么意思呢?就是说,假如我们在内循环中,往比每个数小的那个方向去找(就是对于每个元素,我们统一找比它小1的、小2的、小3的…,而不是大1的,大2的,大3的…),那么如果我们能在该数组中找到有比它大1的元素存在,我们就直接跳过它,就不理它。直到我们在数组中不能够找到有比它大的元素存在,说明它就是边界了,这时我们才开始计数。

这里笔者就对第二种思路进行展开分析了哈。可以确立以下思路:

1)先把数组中所有的数都放到哈希表里面,直接放。

2)然后用几个 if 和 else 就搞定了

然后,对于数组的每一个元素,如果它的value值不是false(一开始初始化的时候都是true),\
判断每个数比它大1的和比它小1的是否在里面
if 比它大1:  if 它的比它大1没有在这数组里面(也就是不存在):then 它是最后一个元素 从它开始计数;再开始判断比它小1的数是否存在【if 比它小1的数在这里面 :then 继续往前找(找小2的、小3的...),并且找的那个元素的\value值赋值false,这样在外层“对于每一个元素中”看到它就会直接跳过了。 else:就不管,跳过】else if 比它大1的数在这里面:then 它不是最后一个元素,别理它,直接跳过

但是我又发现,这个false赋值的好像很鸡肋的样子。因为你会发现最后所有的value值是false的元素,都是"比它大1的数"存在的。那当我在外层循环遍历到这个value值是false的元素的时候,如果跟我不用这个false,我直接去找比它大1的数存不存在,如果存在,那我也仍然是可以直接跳过它,这个查找的时间复杂度也是O(1)。

所以这个false就完全没有必要了。在外层循环,我只要去找比它大1的元素存不存在就行了。实际上我在上述的伪代码中已经去找比它大1的数在不在了。

也就是说,思路简而言之,一句话:对于数组的每个元素,查找:1)比它大1的数不在里面,2)比它小1的数在里面,只有当1)2)条件都满足的时候,我们才开始内循环遍历(就是去找比它小1的、小2的…小n的在不在这里面),否则直接跳过该元素。然后最后找到最长的序列。

3、复杂度

时间复杂度:

以算法的角度来看,

1、把这些数放到哈希表里面,需要O(N);(步骤1)

2、再次遍历一个数组,也是O(N)。 (步骤2)

3、对于哈希表中的每个元素,如果它前一个数不在里面,直接跳过,O(1)。如果在里面,那么会递归去找之前的数,O(N),但是,可以预见这些数在未来的过程(步骤2)中就不会再次去遍历了。

因此是时间复杂度是 O ( N ) O(N) O(N)

不过很多人觉得这种说法可能会有些牵强,那我们换一种说法:

以数组中每个数的视角来去看,它最多会被遍历四次(放进哈希表一次,外层遍历一次,内层遍历一次,被来自比它小1的那个数的查找一次)

这样的话,就显然是 O ( N ) O(N) O(N)了。

空间复杂度:

O ( n ) O(n) O(n),哈希表的空间复杂度,没什么好说的。

4、Code

C++版本:

class Solution {
public:int longestConsecutive(vector<int>& nums) {int Max = 0;unordered_set<int> mp;for(int i = 0;i < nums.size();i++){mp.insert(nums[i]);}for(int i = 0;i < nums.size();i++){if(mp.find(nums[i] + 1) == mp.end()){int length = 1;while(mp.find(nums[i] - length) != mp.end()){length++;}Max = max(Max, length);}else{continue;}}return Max;}
};

Java版本:

class Solution {public int longestConsecutive(int[] nums) {int max = 0;HashSet<Integer> set = new HashSet<>();for (int num : nums) {set.add(num);}for (int num : nums) {if (!set.contains(num + 1)) {int length = 1;while (set.contains(num - length)) {length++;}max = Math.max(max, length);}}return max;}
}

Python版本:

class Solution(object):def longestConsecutive(self, nums):""":type nums: List[int]:rtype: int"""max_length = 0num_set = set(nums)for num in nums:if num - 1 not in num_set:length = 1while num + length in num_set:length += 1max_length = max(max_length, length)return max_length

最后,可以来稍微总结一下,这道题的核心或者价值我认为还是在于用哈希来空间换时间的做法。如果不用哈希,那么判断前一个数在不在里面,将会用到O(N);判断后一个数在不在里面,也需要O(N),那么如果用哈希来查找,这两步的操作就都变成O(1)了->数组的频繁查找工作。至于后面的微调,我们有哈希表,于是乎大家只要去从“第一次遍历过的,第二次就不要遍历了”的角度去考虑就行了,也因为有哈希表,也就能有了这样考虑的余地。顺理成章地就出来了。

如果喜欢我的题解,就给我个关注吧,我会持续为大家带来更优质的分享。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/180796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis高可用集群架构

高可用集群架构 哨兵模式缺点 主从切换阶段&#xff0c; redis服务不可用&#xff0c;高可用不太友好只有单个主节点对外服务&#xff0c;不能支持高并发单节点如果设置内存过大&#xff0c;导致持久化文件很大&#xff0c;影响数据恢复&#xff0c;主从同步性能 高可用集群…

SSM项目管理系统开发oracle10g数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM项目管理系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统 主要采用B/S模式开…

自研基于Xilinx PCIe的高性能多路视频采集与显示控制器

1 概述 视频采集与显示子系统可以实时采集多路视频信号&#xff0c;并存储到视频采集队列中&#xff0c;借助高效的硬实时视频帧出入队列管理和PCIe C2H DMA引擎&#xff0c;将采集到的视频帧实时传递到上位机采集缓冲区。在超带宽视频采集情况下&#xff0c;支持采集丢帧操作…

重温 re:Invent,分享十年成长:我和 re:Invent的故事

文章目录 前言背景我和re:Invent的交际历届峰会主题2012 突破技术垄断2013 革新数据服务2014 更好用的云服务2015 打通最后一-公里2016 迈向云上数据湖时代2017 重构云计算基础2018 云能力的再进化2019 赋能企业云架构服务2020 推动行业数据库服务的演进2021 无可比拟的云架构2…

【开源】基于Vue和SpringBoot的企业项目合同信息系统

项目编号&#xff1a; S 046 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S046&#xff0c;文末获取源码。} 项目编号&#xff1a;S046&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合…

LLM、ChatGPT与多模态必读论文150篇

为了写本 ChatGPT 笔记&#xff0c;我和10来位博士、业界大佬&#xff0c;在过去半年翻了大量中英文资料/paper&#xff0c;读完 ChatGPT 相关技术的150篇论文&#xff0c;当然还在不断深入。 由此而感慨&#xff1a; 读的论文越多&#xff0c;你会发现大部分人对ChatGPT的技…

java List集合(ArrayList,LinkedList,Vector)

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍java List集合的三种实现类ArrayList&#xff0c;LinkedList&#xff0c;Vector以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习…

HassOS使用nmcli设置静态IPv4地址及网关、DNS

目录 显示hass在使用的默认连接显示此连接的所有配置编辑hass默认连接添加静态IP地址添加DNS和网关删除DNS查看IPv4属性保存配置并退出nmcli重载配置 首先控制台登陆Home Assistant OS Welcome to Home Assistant homeassistant login:使用root用户登录&#xff08;无需密码&a…

【数据结构】树与二叉树(廿五):树搜索给定结点的父亲(算法FindFather)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲a. 算法FindFatherb. 算法解析c. 代码实现 3. 代码整合 5.3.1 树的存储结构 5. 左儿子右兄弟链接结构 【数据结构】树与二叉树&#xff08;十九&…

vue实现动态路由菜单!!!

目录 总结一、步骤1.编写静态路由编写router.jsmain.js注册 2.编写permisstions.js权限文件编写permisstions.jsaxios封装的APIstore.js状态库system.js Axios-APIrequest.js axios请求实例封装 3.编写菜单树组件MenuTree.vue 4.主页中使用菜单树组件 总结 递归处理后端响应的…

量子力学:科技前沿的探索与挑战

量子力学:科技前沿的探索与挑战 一、量子力学的魅力与挑战 量子力学是研究微观粒子如电子、光子等行为的物理学分支。与经典力学不同,量子力学描述了一个充满不确定性和概率性的世界。在这个世界里,粒子可以同时处于多个状态,只有当我们对其进行测量时,它才会“选择”一个…

【古月居《ros入门21讲》学习笔记】11_客户端Client的编程实现

目录 说明&#xff1a; 1. 服务模型 2. 实现过程&#xff08;C&#xff09; 创建功能包 创建客户端代码&#xff08;C&#xff09; 配置客户端代码编译规则 编译 运行 3. 实现过程&#xff08;Python&#xff09; 创建客户端代码&#xff08;Python&#xff09; 运行…

Find My电容笔|苹果Find My技术与电容笔结合,智能防丢,全球定位

随着平板电脑的流行&#xff0c;有不少厂商都投入到了电容笔的开发当中&#xff0c;现在的电容笔不仅在精度上有了提高&#xff0c;甚至在笔触和压感上的研究都有进步。电容笔是利用导体材料制作的具有导电特性、用来触控电容式屏幕完成人机对话操作用的笔&#xff0c;电容笔通…

线性可分SVM摘记

线性可分SVM摘记 0. 线性可分1. 训练样本到分类面的距离2. 函数间隔和几何间隔、(硬)间隔最大化3. 支持向量 \qquad 线性可分的支持向量机是一种二分类模型&#xff0c;支持向量机通过核技巧可以成为非线性分类器。本文主要分析了线性可分的支持向量机模型&#xff0c;主要取自…

【藏经阁一起读】(78)__《Apache Tomcat 的云原生演进》

【藏经阁一起读】&#xff08;78&#xff09; __《Apache Tomcat 的云原生演进》 目录 __《Apache Tomcat 的云原生演进》 一、读后感 二、文章知识点摘要 2.1、Tomcat的技术内幕和在喜马拉雅的实践 2.2、GraalVM static compilation in web container application&…

重生奇迹MU魔法师操作技能

重生奇迹MU魔法师增加伤害加点方式 一、智力敏捷加点&#xff1a;2点智力1点敏捷&#xff0c;这种加点就是智敏结合的加点了&#xff0c;属性是不错的&#xff0c;提升了非常多的属性点&#xff0c;智力是偏重输出的&#xff0c;也是法师最常见的一种加点了&#xff0c;输出伤…

随笔(持续更新)

随笔&#xff08;持续更新&#xff09; 1、某个网络有没有连通 要获取某个网站的ip地址&#xff0c;可以通过ping它的域名就可以得到IP地址 例如&#xff1a;我想获取百度的ip地址&#xff08;Windows环境&#xff09; C:\Users\tq>ping www.baidu.com正在 Ping www.a.s…

pdf文件编辑,[增删改查]

pdf文件是投标文件中必不可少的格式&#xff0c;传统的方式先编辑word格式&#xff0c;最后生成pdf&#xff0c;但是有时候需要直接编辑pdf文件&#xff0c;编辑pdf的工具无疑 “adobe acrobat dc”是最好用的之一了 1.把图片文件添加到pdf指定位置&#xff0c;例如把一张图片添…

C++数据结构:图

目录 一. 图的基本概念 二. 图的存储结构 2.1 邻接矩阵 2.2 邻接表 三. 图的遍历 3.1 广度优先遍历 3.2 深度优先遍历 四. 最小生成树 4.1 最小生成树获取策略 4.2 Kruskal算法 4.3 Prim算法 五. 最短路径问题 5.1 Dijkstra算法 5.2 Bellman-Ford算法 5.3 Floyd-…

HCIP-十二、BGP常用属性

十二、BGP常用属性 实验拓扑实验需求及解法1.IP 地址已配置&#xff0c;自行测试直连。2.AS100 中运行 OSPF3.AS200 中运行 ISIS4.运行 BGP5.发布 BGP 路由6.修改起源属性 Origin7.修改 AS-path8.修改本地优先 Local-preference9.修改 MED 实验拓扑 实验需求及解法 本实验模拟…