力扣最热一百题——3.最长连续序列

news2024/11/24 16:59:54

目录

题目链接:128. 最长连续序列 - 力扣(LeetCode)

题目描述

示例

提示

解法一:排序+双指针+剪枝

思路

1. 获取数组长度并进行特判

2. 对数组进行排序

3. 初始化变量

4. 遍历数组并寻找最长连续子序列

5. 返回结果

总结

整体代码

超级优化

解法二:哈希表

乐色思路

高级思路

总结


        今天是力扣哈希题目的最后一道题啦~~~然后今天更新了一篇利用摄像头就可以作为虚拟键盘的文章,源码已经附上,大家可以看看下面是文章链接感谢支持!!! 

上题目


题目链接:128. 最长连续序列 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

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

提示

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109

        其实我在看见这个题目的时候我是觉得挺简单的,因为我的第一个思路非常的简单,直接进行一个排序即可,然后统计出来最长的序列不就好啦,哈哈,于是我就直接开始写代码了。 但是我又看了一眼题目问我能否使用时间复杂度为O(n) 的算法解决,那么应该还有更快的方法,你能想到吗?


解法一:排序+双指针+剪枝

思路

这段代码的目标是找到一个整数数组中最长的连续子序列的长度。我们一步步来分析代码的实现。

1. 获取数组长度并进行特判

        首先,我们获取数组的长度 len。如果数组的长度小于等于1,那么最长的连续子序列的长度就是数组的长度本身(因为只有一个元素或者没有元素)。

2. 对数组进行排序

        为了方便找到连续的子序列,我们先对数组进行排序。排序后,相邻的元素如果差值为1,那么它们就是连续的。

3. 初始化变量

        我们定义一个变量 max,用于存储当前找到的最长连续子序列的长度。初始值为1,因为最短的子序列长度至少为1。

4. 遍历数组并寻找最长连续子序列
  • 我们使用一个 for 循环从数组的第一个元素开始遍历,直到倒数第二个元素。
  • 在每次循环中,我们首先检查剩余的元素是否足够长。如果剩余的元素长度小于当前找到的最长子序列长度 max,那么直接返回 max
  • 我们定义两个指针 low 和 fast,分别指向当前子序列的起始位置和下一个位置。
  • 使用 while 循环来寻找当前起始位置 i 开始的最长连续子序列。
    • 如果遇到相同的元素,我们跳过(去重)。
    • 如果两个元素的差值为1,说明它们是连续的,我们增加当前子序列的长度 curMax,并移动两个指针。
    • 如果两个元素不连续,退出 while 循环。
  • 更新 max,如果当前子序列的长度 curMax 大于 max,则更新 max
5. 返回结果

        最后,遍历完数组后,返回找到的最长连续子序列的长度 max

总结

        这段代码通过排序和双指针法,遍历数组并寻找最长的连续子序列。排序保证了我们只需检查相邻元素是否连续,双指针法则有效地找到每个起始位置的最长连续子序列。通过这种方法,我们可以在 n\log n  的时间复杂度内解决问题。

整体代码

class Solution {
    public int longestConsecutive(int[] nums) {
        // 获取数组的长度
        int len = nums.length;
        // 长度特判
        if (len <= 1){
            return len;
        }
        // 直接先进行排序
        Arrays.sort(nums);
        // 1,2,3,4,100,200
        // 定义max用于存储最长的子序列
        int max = 1;
        for (int i = 0; i < len - 1; i++) {
            // 如果剩余的长度已经没有max的长度还长,那么剩下的就不用比较了
            if (len - i < max){
                return max;
            }
            // 定义当前序列的最长长度
            int curMax = 1;
            // 定义慢指针
            int low = i;
            // 定义快指针
            int fast = i + 1;
            // 进入循环
            while (fast < len){
                // 去重,如果遇到相同的元素就直接跳过
                if (nums[fast] - nums[low] == 0){
                    fast++;
                    low++;
                    continue;
                }
                // 如果前后相差1说明是连续子序列
                if (nums[fast] - nums[low] == 1){
                    curMax++;
                    fast++;
                    low++;
                }else {
                    // 不是直接结束循环
                    break;
                }
            }
            // 如果当前的最长子序列大于全部的最长子序列的大小,更新
            if (curMax > max){
                max = curMax;
            }
        }
        // 全部判断完毕,返回
        return max;
    }
}

        怎么说呢,如果不是有一个剪枝的操作(如果剩余的长度已经没有max的长度还长,那么剩下的就不用比较了),这段代码仍然会超时,所以按理来说还有更好的方式来解决这个问题。

超级优化

        基于上一种解法的思路,进行代码大改如下

class Solution {
    public int longestConsecutive(int[] nums) {
        //0 1 2 3  6 7
        //第一种方法直接暴力
        if (nums.length == 1){
            return 1;
        }
        if (nums.length == 0){
            return 0;
        }
        //对数组进行排序
        Arrays.sort(nums);
        notSeem(nums);
        int len = 1;
        int maxLen = 0;
        for (int i = 1; i < nums.length ; i++) {
            //0 1 1 2也算是0 1 2 三个而不是两个
            if (nums[i] == nums[i - 1] + 1){
                //这里证明当前索引位置和前一个位置连续
                len++;
            }else {
                //断了,不连续了
                if (len > maxLen){
                    maxLen = len;
                }
                //然后清空长度临时存储len
                len = 1;
            }
        }
        if (len > maxLen){
            maxLen = len;
        }
        return maxLen;
    }

    private static int[] notSeem(int[] nums) {
        //这里进行去重操作
        // 1 2 2 3
        //   i   j
        int left = 0;
        int right = 1;

        while (right < nums.length){
            if (nums[left] == nums[right]){
                right++;
                continue;
            }
            nums[++left] = nums[right++];
        }
        int[] ints = new int[left + 1];
        for (int i = 0; i <= left; i++) {
            ints[i] = nums[i];
        }
        return ints;
    }
}

 

        快不快老铁们 !!!!!!!!!!!!!!!!黑子说话!!!!!!!!!!!!


解法二:哈希表

        我们这里其实有很多的寻找的过程,或者说基本上都是一个寻找的过程,那么其实哈希表的用武之地就已经出来了。并且我们这里采用set集合作为哈希表,利用set集合的特性直接实现了去重的效果一举两得。

乐色思路

  • 使用哈希集合去重

    • 首先,将数组中的所有元素存入一个哈希集合 set 中,这样可以自动去除重复的元素。哈希集合的特性是每个元素都是唯一的。
  • 初始化最大长度

    • 定义一个变量 max 来记录最长连续序列的长度。
  • 遍历集合中的每一个元素

    • 对集合中的每一个元素 num 进行处理,尝试找到以这个元素为中心的最长连续序列。
  • 向左右扩展找到连续序列

    • 对于每个 num,分别向左和向右扩展,寻找连续的数字。
    • 定义两个变量 curLMaxcurRMax 来记录当前元素 num 左侧和右侧的最长连续长度。
    • 使用两个循环:
      • 第一个循环向左查找,即 curLeftnum 开始,逐步减小,直到不再有连续的数字为止。
      • 第二个循环向右查找,即 curRightnum 开始,逐步增加,直到不再有连续的数字为止。
  • 更新最大长度

    • 如果当前计算出来的连续序列长度 curLMax + curRMax + 1max 大,则更新 max
  • 返回结果

    • 最终返回 max,即数组中的最长连续序列的长度。



public class longestConsecutive {
    public int longestConsecutive(int[] nums) {
        // 这里采用哈希表的方式来解决一下
        // 由于需要一个去重的效果,所以这里采用set集合
        Set<Integer> set = new HashSet<>();
        // 将数组中的内容存入set,且存入的过程中便已经去重(set集合的特性)
        for (int num : nums) {
            set.add(num);
        }

        // 定义max
        int max = 0;

        // 进入判断环节
        for (Integer num : set) {
            int curLMax = 0;
            int curRMax = 0;
            int curLeft = num;
            int curRight = num;
            // 往左找连续的
            while (set.contains(curLeft - 1)){
                curLMax++;
                curLeft--;
            }
            // 往右找连续的
            while (set.contains(curRight + 1)){
                curRMax++;
                curRight++;
            }
            // 1,2,3,4,100,200
            if (max < curLMax + curRMax + 1){
                max = curLMax + curRMax + 1;
            }
        }
        return max;
    }
}

         给你们看看其中一个用例的夸张程度。经过我的IDEA卡了九九八十一天之后,这个数组的长度高达呃算不出来

 

        代码过长你受得了吗??????? 

         但是难不到我,我直接粘贴到word里,直接搜索逗号有多少个然后最后再+1不就好了,但是我现在的文本框也已经卡了十分钟了。最后的结果是:

        这里99999是它根本显示不完结果太多了,好吧,逆天。


        那么这段代码在到力扣上运行的时候会被他的第69个测试用例卡主,直接超时,但是我**都用哈希表了还会超时,干集贸啊!!!!!???不理解,然后我看了一下官方的操作,他的思路不是找到一个数就直接往两边数,而是只找一段数列的第一个数(也就是不能有比这个数小1的数)作为开头直接开始找,这样的话就只需要循环这种数的数量的次数即可,而不是循环set集合的长度的次数。

        那么下面修改好的思路就来了


高级思路

  • 使用哈希集合去重

    • 首先,将数组中的所有元素存入一个哈希集合 set 中,这样可以自动去除重复的元素。
  • 初始化最大长度

    • 定义一个变量 max 来记录最长连续序列的长度。
  • 遍历集合中的每一个元素

    • 对集合中的每一个元素 num 进行处理,尝试找到以这个元素为起点的最长连续序列。
  • 优化:只从可能的起点开始查找

    • 这里的关键优化点在于,只在当前元素 num 的左边不存在连续元素时,才开始查找。也就是说,只有当 num - 1 不存在于集合中时,才从 num 开始扩展连续序列。这可以避免在中间状态的元素上重复查找,减少了不必要的计算。
  • 向右扩展找到连续序列

    • 对于每个可能的起点 num,从 num 开始,向右查找连续的数字。
    • 使用一个循环向右查找,直到不再有连续的数字为止。
  • 更新最大长度

    • 如果当前计算出来的连续序列长度 curMaxmax 大,则更新 max
  • 返回结果

    • 最终返回 max,即数组中的最长连续序列的长度。
class Solution {
    public int longestConsecutive(int[] nums) {
        // 这里采用哈希表的方式来解决一下
        // 由于需要一个去重的效果,所以这里采用set集合
        Set<Integer> set = new HashSet<>();
        // 将数组中的内容存入set,且存入的过程中便已经去重(set集合的特性)
        for (int num : nums) {
            set.add(num);
        }

        // 定义max
        int max = 0;

        // 进入判断环节
        for (Integer num : set) {
            // 我们只要右边存在连续的,不要左边存在连续的,这样可以节省很多寻找时间
            // 因为左边不存在连续的地方很少,而处于中间状态的很多
            if (!set.contains(num - 1)){
                // 那么现在的数就是只存在右边是连续的数了
                int curMax = 1;
                int curNum = num;
                while(set.contains(curNum + 1)){
                    curMax++;
                    curNum++;
                }
                if (max < curMax){
                    max = curMax;
                }
            }
        }
        return max;
    }
}

 

这段代码的优化之处在于减少了不必要的计算。具体来说:

  • 在原始算法中,对于每一个元素,都会向左右两边扩展查找连续序列,这样会导致大量的重复计算,尤其是在数组中存在很多连续元素时。
  • 在优化后的算法中,通过 if (!set.contains(num - 1)) 这一判断,仅在当前元素的左边不存在连续元素时才开始查找。这样可以确保每个连续序列只会被计算一次,避免了重复计算,从而提高了效率。

        就这个结果而言已经是很不错了,但是还是没有前面方法的


总结

        一个算法题很多思路,很多方法,而且我们真的要学会研究自己的好方法,官方的方法即使优化好也才20+ms,但是我们自己的方式可以达到10+ms几乎一倍的性能提升就给硬件带去了非常宽泛的选择空间,这就是软件的魅力。

        如果大家喜欢,点个关注点个赞支持一下吧~~~~

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

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

相关文章

Linux笔记-对.a静态库的进一步理解(2024-04-09)

过程 问&#xff1a; Linux中生成.a库时候&#xff0c;如果代码里面调用了一些只引用未定义的函数&#xff0c;gcc不报错&#xff0c;但能生成对应的.a文件&#xff0c;这是为什么&#xff1f;再写一个执行程序去调用.a库时&#xff0c;链接时就会报这个.a库未定义的引用&…

列举excel中调整行高列宽的五种方法

列举excel中调整行高列宽的五种方法 在Excel中调整行高列宽的方法有以下五种&#xff1a; 使用鼠标手动调整行高列宽&#xff1a;将鼠标悬停在行或列的边界上&#xff0c;光标会变成双向箭头&#xff0c;此时按住鼠标左键并拖动边界即可调整行高或列宽。 使用快捷键调整行高列…

node和npm安装;electron、 electron-builder安装

1、node和npm安装 参考&#xff1a; https://blog.csdn.net/sw150811426/article/details/137147783 下载&#xff1a; https://nodejs.org/dist/v20.15.1/ 安装&#xff1a; 点击下载msi直接运行安装 安装完直接cmd打开可以&#xff0c;默认安装就已经添加了环境变量&…

vue3通过html2canvas dom转图片复制到剪贴板和dom转图片并下载

代码实现 <template><div class"page"><div id"to-img-dom"><strong>我是图片标题</strong><p>我是内容&#xff0c;我是内容&#xff0c;我是内容&#xff0c;我是内容&#xff0c;我是内容&#xff0c;我是内容&am…

Jupyter notebook 快速入门

1、什么是jupyter notebook Jupyter Notebook是一个交互式笔记本环境&#xff0c;可以在其中同时编写和运行代码&#xff0c;以及进行数据分析和可视化。它支持多种编程语言&#xff08;如Python、R、Julia等&#xff09;&#xff0c;并提供了丰富的功能和工具供用户使用。Jup…

如何减少网站延迟?

什么是网络延迟&#xff1f; Web 延迟描述了网站响应用户请求所花费的时间。它是网络性能的一个重要因素&#xff0c;因为它决定了用户访问网站内容并与之交互的速度。当延迟很高时&#xff0c;网站会变得缓慢且反应迟钝&#xff0c;从而导致用户不满意。延迟可能由多种因素引…

如何用 WinDbg 调试Linux上的 .NET程序

一&#xff1a;背景 1. 讲故事 最新版本 1.2402.24001.0 的WinDbg真的让人很兴奋&#xff0c;可以将自己伪装成 GDB 来和远程的 GDBServer 打通来实现对 Linux 上 .NET程序进行调试&#xff0c;这样就可以继续使用熟悉的WinDbg 命令&#xff0c;在这个版本中我觉得 WinDbg 不…

数据结构C++——二叉树和树

文章目录 一、树二、二叉树三、二叉树的特性特性1. 包含n (n> 0 )个元素的二叉树边数为n-1特性 2: 若二叉树的高度为h,h≥0,则该二叉树最少有h个元素,最多有2^h-1 个元素特性3:包含n(n≥0)个元素的二叉树的高度最大为n,最小为ceiling(log2(n+1))特性4. 完全二叉树四、二…

基于微信小程序+SpringBoot+Vue的高校校园交友(带1w+文档)

基于微信小程序SpringBootVue的高校校园交友(带1w文档) 基于微信小程序SpringBootVue的高校校园交友(带1w文档) 在目前的情况下&#xff0c;可以引进一款高校校园交友微信小程序这样的现代化管理工具&#xff0c;这个工具就是解决上述问题的最好的解决方案。它不仅可以实时完成…

代码随想录 day 21 二叉树

第六章 二叉树part08 669. 修剪二叉搜索树 这道题目比较难&#xff0c;比 添加增加和删除节点难的多&#xff0c;建议先看视频理解。 题目链接/文章讲解&#xff1a; https://programmercarl.com/0669.%E4%BF%AE%E5%89%AA%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html…

行业、客户双认可!路凯智行精彩亮相新疆煤博会

7月18日&#xff0c;2024丝路矿业合作论坛、第14届中国新疆国际矿业与装备博览会、第19届中国新疆国际煤炭工业博览会在新疆国际会展中心举行&#xff0c;此次论坛和展会吸引了全国26个省区市和德国、美国、日本、挪威、芬兰、法国、巴基斯坦、俄罗斯、白俄罗斯、乌兹别克斯坦等…

企业如何应对大模型落地的四大挑战?

近年来&#xff0c;人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;技术取得了突飞猛进的发展&#xff0c;其中大模型&#xff08;如GPT-3、GPT-4、BERT等&#xff09;因其强大的数据处理和分析能力&#xff0c;受到了企业的高度重视。大模型在自然语…

使用nginx实现一个端口和ip访问多个vue前端

前言&#xff1a;由于安全组要求&#xff0c;前端页面只开放一个端口&#xff0c;但是项目有多个前端&#xff0c;此前一直使用的是一个前端使用单独一个端口进行访问&#xff0c;现在需要调整。 需要实现&#xff1a;这里以80端口为例&#xff0c;两个前端分别是&#xff1a;p…

[用AI日进斗金系列]用码上飞在企微接单开发一个项目管理系统!

今天是【日进斗金】系列的第二期文章。 先给不了解这个系列的朋友们介绍一下&#xff0c;在这个系列的文章中&#xff0c;我们将会在企微的工作台的“需求发布页面”中寻找有软件开发需求的用户 并通过自研的L4级自动化智能软件开发平台「码上飞CodeFlying」让AI生成应用以解…

可以免费合并pdf的软件 合并pdf文件的软件免费 合并pdf的软件免费

在数字化办公的今天&#xff0c;pdf格式因其稳定性和跨平台兼容性被广泛使用。然而&#xff0c;当我们需要将多个 pdf 文件合并为一个时&#xff0c;却往往感到力不从心。本文将为你介绍几款强大的pdf文件合并软件&#xff0c;让你轻松管理文档。 方法一、使用pdf转换器 步骤1…

Vue3 对比 Vue2

相关信息简介2020年9月18日&#xff0c;Vue.js发布3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;海贼王&#xff09; 2 年多开发, 100位贡献者, 2600次提交, 600次 PR、30个RFC Vue3 支持 vue2 的大多数特性 可以更好的支持 Typescript&#xff0c;提供了完整的…

亚马逊跟卖北美选品ERP操作注意事项,商标自动查询,可...

新手在美国站选品有哪几种方式呢&#xff1f;美国商标局复查未备案准确度100%。 今天来讲下美国品牌未备案准不准确。 点开采集任务&#xff0c;站点选择美国&#xff0c;有五种采集方式&#xff1a;关键词、店铺链接、类目asin&#xff0c;选择完之后点确定。 选择asin采集…

【Docker】Docker-consul容器服务自动发现与注册

目录 一.Consul概述 1.解决了什么问题 2.什么叫微服务或者注册与发现 3.consul的模式 4.相关命令 二.consul 部署 1.consul服务器部署 2.部署docker容器 3.Nginx负载均衡器 3.1.安装启动nginx 3.2.配置nginx负载均衡 3.3.创建配置consul complate模板文件 3.4.添加…

只需三步,即可使用 Kafka 托管服务快速部署微服务架构应用

微服务架构的应用程序的特点是将其组件组织得能够独立地进行开发、测试、部署和扩展。DigitalOcean App Platform&#xff08;应用平台&#xff09;的目标是通过允许用户在同一应用上添加多个组件&#xff0c;简化这一架构模型&#xff0c;使其更加平滑和易于管理。 一个简单的…

【Zotero插件】Zotero Tag为文献设置阅读状态 win11下相关设置

【Zotero插件设置】Zotero Tag为文献设置阅读状态 win11下相关设置 1.安装Zotero Tag1.1安装1.2配置1.3 win11的相关设置1.3.1 字体安装 参考教程 2.支持排序的标注参考教程 1.安装Zotero Tag 1.1安装 Zotero Tag插件下载链接安装方法&#xff1a;Zotero–》工具–》附加组件…