【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 = 0
        num_set = set(nums)
        
        for num in nums:
            if num - 1 not in num_set:
                length = 1
                
                while num + length in num_set:
                    length += 1
                    
                max_length = max(max_length, length)
        
        return max_length

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

stm32 TIM

一、TIM简介 TIM&#xff08;Timer&#xff09;定时器定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断。16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时定时器不仅具备基本的定时中断功能&…

前端 | iframe框架标签应用

文章目录 &#x1f4da;嵌入方式&#x1f4da;图表加载显示&#x1f4da;100%嵌入及滑动条问题&#x1f4da;加载动画保留 前情提要&#xff1a; 计划用iframe把画好的home1.html&#xff08;echarts各种图表组成的html数据大屏&#xff09;嵌入整合到index.html&#xff08;搭…

只需十分钟快速入门Python,快速了解基础内容学习。零基础小白入门适用。

文章目录 简介特点搭建开发环境版本hello world注释文件类型变量常量数据类型运算符和表达式控制语句数组相关函数相关字符串相关文件处理对象和类连接mysql关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源…

摇滚史密斯2014重置版外接声卡

摇滚史密斯2014重置版外接声卡 前提 由于rs_asio是通过模拟原厂线的方法来使游戏可以支持声卡的&#xff0c;因此&#xff0c;声卡的采样频率需要满足原厂线要求&#xff0c;即采样率可以设置为 48000 Hz。 我使用的是 Sonic Cube 这款声卡&#xff0c;非常幸运&#xff0c;…

汽车电子 -- 车载ADAS之FCW(前方碰撞预警)

相关法规文件: FCW: GB∕T 33577-2017 智能运输系统 车辆前向碰撞预警系统 性能要求和测试规程 一、前方碰撞预警 FCW&#xff08; Forward Collision Warning&#xff09; 参看&#xff1a;法规标准-GB/T 33577标准解读(2017版) 1、状态机 系统关闭 当车辆前向碰撞预警系…

unity学习笔记07

一、组件 有几个物体他们之间有着重复的功能&#xff0c;该如何避免重复的去写代码&#xff1f; 可以将一些相同的功能写成一个组件&#xff0c;也就是组件就等同于功能。 什么是组件&#xff1f; 在Unity中&#xff0c;游戏物体是不具备任何功能的&#xff0c;如果想要为其…

香港科技大学数据建模(MSc DDM)硕士学位项目(2024年秋季入学)招生宣讲会-武汉专场

时间&#xff1a;2023 年12 月 8 日&#xff08;周五&#xff09; 15:00 地点&#xff1a;华中科技大学大学生活动中心B座303 嘉宾教授&#xff1a;张锐 教授 项目旨在培养科学或工程背景的学员从数据中提取信息的数据建模能力&#xff0c;训练其拥有优秀的解难和逻辑思考与分…

3.OpenResty系列之Nginx反向代理

1. Nginx简介 Nginx (engine x) 是一款轻量级的 Web 服务器 、反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器 什么是反向代理&#xff1f; 反向代理&#xff08;Reverse Proxy&#xff09;方式是指以代理服务器来接受 internet 上的连接请求&#x…

金山办公前端二面

1. react 和 vue的区别 还有jquery&#xff1f; &#xff08;1&#xff09; jquery 和 vue、react 的区别&#xff1a; vue 和 react : 数据和视图分离 以数据驱动视图&#xff0c;只关心数据变化 dom 操作被封装&#xff08;数据驱动&#xff09; jquery&#xff1a;依靠 do…

python环境的搭建+pytharm安装教程

一、Anaconda安装 1、去官网下载anaconda >百度搜索anaconda按回车键 >找到官网地址进去&#xff08;注意看网址&#xff09; >下载位置 2、安装anaconda 具体就安装步骤就不演示了&#xff08;写文章时已经安装好了&#xff09; 二、pycharm安装 1、去官网下载py…

Redis 基本命令—— 超详细操作演示!!!

内存数据库 Redis7—— Redis 基本命令 三、Redis 基本命令&#xff08;下&#xff09;3.8 benchmark 测试工具3.9 简单动态字符串SDS3.10 集合的底层实现原理3.11 BitMap 操作命令3.12 HyperLogLog 操作命令3.13 Geospatial 操作命令3.14 发布/订阅命令3.15 Redis 事务 四、Re…

【C语言加油站】函数栈帧的创建与销毁 #保姆级讲解

函数栈帧的创建与销毁 导言一、计算机硬件1.冯•诺依曼机基本思想2.冯•诺依曼机的特点&#xff1a;3.存储器3.1 分类3.2 内存的工作方式3.3 内存的组成 4.寄存器4.1 基本含义4.2 寄存器的功能4.3 工作原理4.4 分类4.4.1 通用寄存器组AX(AH、AL)&#xff1a;累加器BX(BH、BL)&a…

第七节HarmonyOS UIAbility生命周期以及启动模式

一、UIAbility生命周期 为了实现多设备形态上的裁剪和多窗口的可扩展性&#xff0c;系统对组件管理和窗口管理进行了解耦。UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态&#xff0c;WindowStageCreate和WindowStageDestroy为窗口管理器&#xff08…

堆结构的应用:随时取得数据流中的中位数

大根堆和小根堆配合 实现 第一个数字直接入大根堆 对于后面的数字&#xff0c; 如果数字 < 大根堆的堆顶&#xff0c;这个数字入大根堆 否则入小根堆 在数字入堆的同时&#xff0c;进行大根堆与小根堆的大小的比较&#xff0c;一旦它们两个的大小之差 2&#xff0c;较大…

【浅尝C++】C++类的6大默认成员函数——构造、析构及拷贝构造函数

&#x1f388;归属专栏&#xff1a;浅尝C &#x1f697;个人主页&#xff1a;Jammingpro &#x1f41f;记录一句&#xff1a;好想摆烂&#xff0c;又好想学习~~ 文章前言&#xff1a;本篇文章简要介绍C类的构造函数、析构函数及拷贝构造函数&#xff0c;介绍每个小点时&#xf…

java+python农村集体产权管理系统php+vue

注册、登陆该系统根据操作权限的不同分为管理员和用户两种&#xff0c;新用户在登陆前要进行用户注册&#xff0c;注册完成后方可进行登陆。 本次设计的关键问题处理&#xff0c;主要有如下几点&#xff1a; (1&#xff09;本次开发&#xff0c;采用主流Thinkphp框架进行开发&a…

linux进入telnet和推出telnet

安装telnet centos7 yum install -y telnet ubuntu apt install -y telnet 进入telnet telnet ip port 退出telnet 1. 按下下面的组合键 ctrl] 2. 输入下面命令推出 quit

Go 语言中 sync 包的近距离观察

让我们来看看负责提供同步原语的 Go 包&#xff1a;sync。 sync.Mutex sync.Mutex 可能是 sync 包中被广泛使用的原语。它允许对共享资源进行互斥操作&#xff08;即不允许同时访问&#xff09;&#xff1a; mutex : &sync.Mutex{}mutex.Lock() // Update shared variab…

【linux】基本指令(中篇)

echo指令 将引号内容打印到显示屏上 输出的重定向 追加的重定向 输出的重定向 我们学习c语言的时候当以写的方式创建一个文件&#xff0c;就会覆盖掉该文件之前的内容 当我们以追加的方式打开文件的时候&#xff0c;原文件内容不会被覆盖而是追加 more指令 10.more指令…

YOLOv8优化策略:自适应改变核大小卷积AKConv,效果优于标准卷积核和DSConv |2023.11月最新成果

🚀🚀🚀本文改进: AKConv 中,通过新的坐标生成算法定义任意大小的卷积核的初始位置。 为了适应目标的变化,引入了偏移量来调整每个位置的样本形状。 此外,我们通过使用具有相同大小和不同初始采样形状的 AKConv 来探索神经网络的效果。 AKConv 通过不规则卷积运算完成…