LeedCode刷题---子数组问题

news2025/1/17 18:01:45

顾得泉:个人主页

个人专栏:《Linux操作系统》  《C/C++》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、最大子数组和

题目链接:最大子数组和 

题目描述

       给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和

       子数组 是数组中的一个连续部分

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

解法

1.状态表示:

       对于线性dp,我们可以用「经验+题目要求」来定义状态表示:

       i.以某个位置为结尾,巴拉巴拉;

       ii.以某个位置为起点,巴拉巴拉。

       这里我们选择比较常用的方式,以「某个位置为结尾」

结合「题目要求」,定义一个状态表示:

       dp[i]表示:以i位置元素为结尾的「所有子数组」中和的最大和

2.状态转移方程:

       dp[i]的所有可能可以分为以下两种

        i.子数组的长度为1:此时dpLi= nums[i];

        ii.子数组的长度大于1:此时 dp[i]应该等于以i - 1做结尾的「所有子数组」中和的最大值再加上nums[i],也就是dp[i - 1]+ nums[i]

       由于我们要的是最大值,因此应该是两种情况下的最大值,因此可得转移方程:

       dp[i] = max (nums[i],dp[i - 1]+ nums[i])

3.初始化:

       可以在最前面加上一个「辅助结点」,帮助我们初始化。使用这种技巧要注意两个点:

       i.辅助结点里面的值要「保证后续填表是正确的」;

       ii.「下标的映射关系」

       在本题中,最前面加上一个格子,并且让dp[0] = 即可

4.填表顺序:

       根据「状态转移方程」易得,填表顺序为「从左往右」

5.返回值:

       状态表示为「以1为结尾的所有子数组」的最大值,但是最大子数组和的结尾我们是不确定的。因此我们需要返回整个dp表中的最大值

代码实现

class Solution {
public:
    int maxSubArray(vector<int>& nums) 
    {
        int n = nums.size();
        vector<int> dp(n + 1);
        int ret = INT_MIN;
        for(int i = 1; i <= n; i++)
        {
        dp[i] = max(nums[i - 1], dp[i - 1] + nums[i - 1]);
        ret = max(ret, dp[i]);
        }
        return ret;
    }
};

二、环形子数组的最大和

题目链接:环形子数组的最大和

题目描述

       给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 

       环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 

       子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 

示例 1:

输入:nums = [1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3

示例 2:

输入:nums = [5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10

示例 3:

输入:nums = [3,-2,2,-3]
输出:3
解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3

提示:

  • n == nums.length
  • 1 <= n <= 3 * 104
  • -3 * 104 <= nums[i] <= 3 * 104

解法

       本题与「最大子数组和」的区别在于,考虑问题的时候不仅要分析「数组内的连续区域」,还要考虑「数组首尾相连」的一部分。结果的可能情况分为以下两种:

       i.结果在数组的内部,包括整个数组;

       ii.结果在数组首尾相连的一部分上

       其中,对于第一种情况,我们仅需按照「最大子数组和」的求法就可以得到结果,记为 fmax。对于第二种情况,我们可以分析一下:

       i.如果数组首尾相连的一部分是最大的数组和,那么数组中间就会空出来一部分;

       ii.因为数组的总和sum是不变的,那么中间连续的一部分的和一定是最小的;因此,我们就可以得出一个结论,对于第二种情况的最大和,应该等于sum - gmin

       其中,gmin表示数组内的「最小子数组和」

       两种情况下的最大值,就是我们要的结果

       但是,由于数组内有可能全部都是负数,第一种情况下的结果是数组内的最大值(是个负数),第二种情况下的 gmin == sum,求的得结果就会是0。若直接求两者的最大值,就会是0。但是实际的结果应该是数组内的最大值。对于这种情况,我们需要特殊判断一下

       由于「最大子数组和」的方法已经讲过,这里只提一下「最小子数组和」的求解过程,其实与「最大子数组和」的求法是一致的。用f表示最大和,g表示最小和

1.状态表示:

       g[i]表示:以i做结尾的所有子数组中和的最小值

2.状态转移方程:

       g[i]的所有可能可以分为以下两种:

       i.子数组的长度为1 :此时g[i] = nums[i] ;

       ii.子数组的长度大于1∶此时 g[i应该等于以i - 1做结尾的「所有子数组」中和的最小值再加上nums[i],也就是g[i - 1] + nums[i]

       由于我们要的是最小子数组和,因此应该是两种情况下的最小值,因此可得转移方程:

       g[i] = min( nums[i],g[i - 1]+ nums[i])

3.初始化:

       可以在最前面加上一个辅助结点,帮助我们初始化。使用这种技巧要注意两个点:

       i.辅助结点里面的值要保证后续填表是正确的;

       ii.下标的映射关系

       在本题中,最前面加上一个格子,并且让g[0]= 0即可

4.填表顺序:

根据状态转移方程易得,填表顺序为「从左往右」

5.返回值:

        a.先找到f表里面的最大值->fmax ;

        b.找到g表里面的最小值-> gmin ;

        c.统计所有元素的和->sum ;

        d.返回sum == gmin ? fmax : max ( fmax, sum - gmin)

代码实现

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) 
    {
        int n = nums.size();
        vector<int> f(n + 1), g(n + 1);
        int fmax = INT_MIN, gmin = INT_MAX, sum = 0;
        for(int i = 1; i <= n; i++)
        {
            int x = nums[i - 1];
            f[i] = max(x, x + f[i - 1]);
            fmax = max(fmax, f[i]);
            g[i] = min(x, x + g[i - 1]);
            gmin = min(gmin, g[i]);
            sum += x;
        }
        return sum == gmin ? fmax : max(fmax, sum - gmin);
    }
};

三、乘积最大子数组

题目链接:乘积最大子数组

题目描述

       给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积

       测试用例的答案是一个 32-位 整数

       子数组 是数组的连续子序列

示例 1:

输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6

示例 2:

输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组

提示:

  • 1 <= nums.length <= 2 * 104
  • -10 <= nums[i] <= 10
  • nums 的任何前缀或后缀的乘积都 保证 是一个 32-位 整数

解法

       这道题与「最大子数组和」非常相似,我们可以效仿着定义一下状态表示以及状态转移:

       i. dp[i]表示以i为结尾的所有子数组的最大乘积,

       ii. dp[i] = max ( nums[i],dp[i - 1]* nums[i]);

       由于正负号的存在,我们很容易就可以得到,这样求dp[i的值是不正确的。因为 dp[i -1]的信息并不能让我们得到dp[i]的正确值。比如数组[-2,5,-2],用上述状态转移得到的dp数组为[-2,5,-2],最大乘积为5。但是实际上的最大乘积应该是所有数相乘,结果为20

       究其原因,就是因为我们在求dp[2]的时候,因为nums[2]是一个负数,因此我们需要的是「 i - 1位置结尾的最小的乘积(-10)」,这样一个负数乘以「最小值」,才会得到真实的最大值。因此,我们不仅需要一个「乘积最大值的dp表」,还需要一个「乘积最小值的dp表」

1.状态表示:

       f[i]表示:以i结尾的所有子数组的最大乘积

       g[i]表示:以i结尾的所有子数组的最小乘积

2.状态转移方程:

       遍历每一个位置的时候,我们要同步更新两个dp数组的值。

       对于f[i],也就是「以为结尾的所有子数组的最大乘积」,对于所有子数组,可以分为下面三种形式:

       i.子数组的长度为1,也就是nums[i] ;

       ii.子数组的长度大于1,但nums[i] > 0,此时需要的是i - 1为结尾的所有子数组的最大乘积f[i - 1],再乘上nums[i],也就是nums[i] * f[i - 1];

       ili.子数组的长度大于1,但nums[i]<0,此时需要的是i - 1为结尾的所有子数组的最小乘积g[i - 1],再乘上nums[i],也就是nums[i] * g[i - 1];(如果nums[i] = 0,所有子数组的乘积均为0,三种情况其实都包含了)

       综上所述,f[i] = max(nums[i],max(nums[i] * f[i - 1],nums[i] * g[i -1]))。

       对于g[i],也就是「以1为结尾的所有子数组的最小乘积」,对于所有子数组,可以分为下面三种形式:

       i.子数组的长度为1,也就是nums[i];   

       ii.子数组的长度大于1),但nums[i] > 0,此时需要的是i – 1为结尾的所有子数组的最小乘积g[i - 1],再乘上nums[i],也就是nums[i] * g[i - 1];

       iii.子数组的长度大于1,但nums[i] < 0,此时需要的是i - 1为结尾的所有子数组的最大乘积f[i - 1],再乘上nums[i],也就是nums[i] * f[i - 1];

       综上所述,g[i] = min(nums[i],min(nums[i] * f[i - 1],nums[i] * g[i -1]))

       (如果nums[i] = 0,所有子数组的乘积均为0,三种情况其实都包含了)

3.初始化:

       可以在最前面加上一个辅助结点,帮助我们初始化。使用这种技巧要注意两个点:

       i.辅助结点里面的值要保证后续填表是正确的;

       ii.下标的映射关系。

       在本题中,最前面加上一个格子,并且让f[o]=g[o]=1即可

4.填表顺序:

       根据状态转移方程易得,填表顺序为从左往右,两个表一起填

5.返回值:

       返回f表中的最大值

代码实现

class Solution {
public:
    int maxProduct(vector<int>& nums) 
    {
        int n = nums.size();
        vector<int> f(n + 1), g(n + 1);
        f[0] = g[0] = 1;
        int ret = INT_MIN;
        for(int i = 1; i <= n; i++)
        {
            int x = nums[i - 1], y = f[i - 1] * nums[i - 1], z = g[i - 1] * nums[i - 1];
            f[i] = max(x, max(y, z));
            g[i] = min(x, min(y, z));
            ret = max(ret, f[i]);
        }
        return ret;
    }
};

 结语:今日的刷题分享到这里就结束了,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 

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

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

相关文章

开发猿的平平淡淡周末---2023/12/3

2023/12/3 天气晴 温度适宜 AM 早安八点多的世界&#xff0c;起来舒展了下腰&#xff0c;阳光依旧明媚&#xff0c;给平淡的生活带来了一丝暖意 日常操作&#xff0c;喂鸡&#xff0c;时政&#xff0c;洗漱&#xff0c;恰饭&#xff0c;肝会儿游戏 看会儿手机 ___看累…

“此应用专为旧版android打造,因此可能无法运行”,问题解决方案

当用户在Android P系统上打开某些应用程序时&#xff0c;可能会弹出一个对话框&#xff0c;提示内容为&#xff1a;“此应用专为旧版Android打造&#xff0c;可能无法正常运行。请尝试检查更新或与开发者联系”。 随着Android平台的发展&#xff0c;每个新版本通常都会引入新的…

[英语学习][6][Word Power Made Easy]的精读与翻译优化

[序言] 针对第18页的阅读, 进行第二次翻译优化以及纠错, 这次译者的翻译出现的严重问题: 没有考虑时态的变化导致整个翻译跟上下文脱节, 然后又有偷懒的嫌疑, 翻译得很随意. [英文学习的目标] 提升自身的英语水平, 对日后编程技能的提升有很大帮助. 希望大家这次能学到东西,…

基于SSM的职业高中智慧作业试题系统设计

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;JSP 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 一、…

基于Java SSM框架实现师生交流答疑作业系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现师生交流答疑作业系统演示 摘要 在新发展的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;人们对师生交流平台越来越重视&#xff0c;更好的实…

【5G PHY】5G NR 如何计算资源块的数量?

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

分享一个国内可用的免费AI-GPT网站

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 我们也忍不住做了一个基于ChatGPT的网站&#xff0c;可以免登陆&#xff01;&#xff01;国内可直接对话AI&#xff0c;也有各种提供工作效率的工具供大家使用。 可以这…

Python的模块与库,及if __name__ == ‘__main__语句【侯小啾python领航班系列(二十四)】

Python的模块与库,及if name == __main__语句【侯小啾python领航班系列(二十四)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ…

c# OpenCV 读取、显示和写入图像(二)

读取、显示和写入图像是图像处理和计算机视觉的基础。即使在裁剪、调整大小、旋转或应用不同的滤镜来处理图像时&#xff0c;您也需要先读取图像。因此&#xff0c;掌握这些基本操作非常重要。 imread()读取图像imshow()在窗口中显示图像imwrite()将图像保存到文件目录里 我们…

Python:私人定制密码保险库 - Vault

简介&#xff1a;Vault是一种用于安全访问机密的工具。秘密是您想要严格控制访问权限的任何内容&#xff0c;例如API密钥、密码、证书等等。Vault为任何机密提供了统一的界面&#xff0c;同时提供了严格的访问控制并记录了详细的审核日志。 历史攻略&#xff1a; Python&…

Android 相机库CameraView源码解析 (三) : 滤镜相关类说明

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…

【springboot】启动失败 Failed to start bean ‘webServerStartStop‘

lsof -i&#xff1a;xxx 发现端口被占用 kill掉该进程

大模型的RPA应用 | 代理流程自动化(APA),开启智能自动化新纪元

随着技术创新的持续推进&#xff0c;自动化技术已经变得至关重要&#xff0c;成为驱动企业和社会向前发展的核心动力。在自动化的里程碑中&#xff0c;机器人流程自动化&#xff08;RPA&#xff09;已经有效地将简单、重复且规则性的任务自动化。可是随着对处理更为复杂、多变且…

【学习笔记】机器学习——GAN

提出于2014年。 GAN由两个神经网络组成&#xff1a;一个试图生成看起来与训练数据相似数据的生成器&#xff0c;以及一个试图从虚假数据中分辨出真实数据的判别器。生成器和判别器在训练期间相互竞争。 对抗训练&#xff08;训练竞争性网络&#xff09;是一种重要的机器学习思想…

循环神经网络RNN及其变体LSTM、GRU

1. 背景 RNN(Recurrent Neural Networks) CNN利用输入中的空间几何结构信息&#xff1b;RNN利用输入数据的序列化特性。 2. SimpleRNN单元 传统多层感知机网络假设所有的输入数据之间相互独立&#xff0c;但这对于序列化数据是不成立的。RNN单元用隐藏状态或记忆引入这种依赖…

2024美赛数学建模资料---100%获奖资料

很好的教程了 一共二十四章 每一章都是一个模型 并且有matlab编程编码 第一章 线性规划 第二章 整数规划 第三章 非线性规划 第四章 动态规划 第五章 图与网络 第六章 排队论 第七章 对策论 第八章 层次分析法 第九章 插值与拟合 第十章 数据的统计描述和分析 第十一章…

小心处理 C++ 静态变量中的陷阱

小心处理 C 静态变量中的陷阱 函数中的 static 变量 static 变量的作用 C 中 static 关键字的最后一个用途是在函数内创建局部变量&#xff0c;这些变量在其作用域内退出和进入时保持其值。函数内的 static 变量类似于只能从该函数访问的全局变量。static 变量的一个常见用途…

JS前端逆向

前言 js逆向一直没有相关了解&#xff0c;虽然目前渗透遇见的不是很多&#xff0c;大多数遇见的要么不加密&#xff0c;要么无法实现其加密流程&#xff0c;不过最近看到了一个较为简单的站点正好能够逆向出来&#xff0c;就做了简单记录。本文旨在介绍js逆向的一些基础思路&am…

超硬核解析Mybatis动态代理原理!只有接口没实现也能跑?

文章目录 前言Mybatis dao层两种实现方式的对比原始Dao开发原始Dao开发的弊端 基于Mapper动态代理的开发方式 Mybatis动态代理实现方式的原理解析动态代理调用链路解析先给出链路调用结果1、调用方法的开始&#xff1a;session.getMapper2、DeaultSqlSession的getMapper3、Conf…

JavaWeb 带条件的分页查询

最终效果图 注意&#xff1a;没有带条件的时候 默认的是第一页数据 条件是组合的 sql->sql的动态变换 注意第二次查询的时候回显问题 就是填完条件后显示完当页数据ok 但是我点击第二页的时候条件还存在着 此时ListSerevlet不仅要拿到页码 页容量 还要拿到三个条件参数 封装…