LeedCode刷题---子数组问题

顾得泉:个人主页

个人专栏:《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.mzph.cn/news/193885.shtml

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

相关文章

【力扣】——可获得的最大点数(滑动窗口)

几张卡牌 排成一行&#xff0c;每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。 每次行动&#xff0c;你可以从行的开头或者末尾拿一张卡牌&#xff0c;最终你必须正好拿 k 张卡牌。 你的点数就是你拿到手中的所有卡牌的点数之和。 给你一个整数数组 cardPoi…

[山东大学操作系统课程设计]实验三

0.写在前面(重点) 由于一些突发事件&#xff0c;导致目前大家手里或多或少都有了完整版的答案了。甚至很多学长学姐们写的代码远比我写的要好很多。 但是这个系列我觉得还是稍微坚持下去一点&#xff0c;或许某些地方可以帮到未来的同学们。 还是那句话&#xff0c;有需要可…

Spring MVC数据绑定的几种方法(一)

这篇文章包含spring mvc的默认数据类型绑定和简单数据类型绑定。内容来自实验。 准备&#xff1a; &#xff08;1&#xff09;在IDEA环境中从archetye创建webapp类型的maven项目exp6。 &#xff08;2&#xff09;在src\main目录下创建并标注java源代码文件夹和resources资源文…

NVIDIA GPU Operator install in kubernetes

文章目录 1. 简介2. Kubernetes 安装3. OS配置4. Docker Engine&#xff0c;cri-dockerd安装5. 安装 kubeadm6. GPU-Operator安装 1. 简介 Kubernetes通过设备插件框架提供对特殊硬件资源的访问&#xff0c;如NVIDIA GPU、⽹卡、Infiniband适配器和其他设备。但是&#xff0c;…

基于Java SSM框架实现实现四六级英语报名系统项目【项目源码+论文说明】

基于java的SSM框架实现四六级英语报名系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个高校四六级报名管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作…

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

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

详解Spring对Mybatis等持久化框架的整合

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

自带灯效的气传导耳机,声音当然好听,哈氪聆光体验

现在市场上的蓝牙耳机种类繁多&#xff0c;入耳式的算是主流&#xff0c;但不太适合户外使用 &#xff0c;我平时出门健身、散步的时候&#xff0c;更喜欢用气传导耳机。气传导耳机通常采用挂耳式的设计&#xff0c;耳机不入耳&#xff0c;佩戴舒适度更好&#xff0c;而且稳定性…

Linux安装mongodb数据库(详细)

一、下载安装包 本文使用 tgz 方式,根据服务器类型在官网下载 MongoDB 安装包。官方地址&#xff1a;https://www.mongodb.com/try/download/community 下载方式如图所示&#xff1a; 选择版本 关于 MongoDB 的版本选择&#xff0c;参见如下版本差异&#xff1a; 1、将从官…

Java基本数据类型详解

✨个人主页&#xff1a;全栈程序猿的CSDN博客 &#x1f4a8;系列专栏&#xff1a;Java从入门到精通 ✌座右铭&#xff1a;编码如诗&#xff0c;Bug似流星&#xff0c;持续追求优雅的代码&#xff0c;解决问题如同星辰般自如 Java是一种强类型语言&#xff0c;数据类型在程序中起…

JS不同运算符下的隐式类型转换

目录 运算符 逻辑运算符&#xff08;&&、||、!&#xff09;和 条件表达式&#xff08;if、三元表达式&#xff09; 逻辑运算符 条件表达式 算数运算符&#xff08;*、/、- %、&#xff09;和 关系运算符&#xff08;>、<、、!&#xff09; 算数运算符 关系…

Python实战:批量加密Excel文件指南

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python实战&#xff1a;批量加密Excel文件指南&#xff0c;全文3800字&#xff0c;阅读大约10分钟。 在日常工作中&#xff0c;保护敏感数据是至关重要的。本文将引导你通过…

栈和队列的OJ题--13.用队列实现栈

13. 用队列实现栈 225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; /*解题思路&#xff1a; 此题可以用两个队列去实现一个栈&#xff0c;每次始终保持一个队列为空&#xff0c; 入栈操作相当于给非空队列进行入队操作 出栈操作相当于非空队列的队尾元素出队&…

细说CountDownLatch

CountDownLatch 概念 CountDownLatch可以使一个获多个线程等待其他线程各自执行完毕后再执行。 CountDownLatch 定义了一个计数器&#xff0c;和一个阻塞队列&#xff0c; 当计数器的值递减为0之前&#xff0c;阻塞队列里面的线程处于挂起状态&#xff0c;当计数器递减到0时…

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

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

工作几年了,你真的懂 Redis 嘛?

大家好&#xff0c;我是伍六七。一个专注于输出 AI 编程内容的在职大厂资深程序员&#xff0c;全国最大 AI 付费社群破局初创合伙人&#xff0c;关注我一起破除 35 诅咒。 Redis 基本上是大部分技术公司都会使用的缓存框架&#xff0c;但是我发现很多程序员其实并不懂 Redis。 …

SSM新闻发布管理系统

SSM毕设分享 序号1&#xff1a;SSM新闻发布管理系统 1 项目简介 Hi&#xff0c;各位同学好&#xff0c;这里是郑师兄&#xff01; 今天向大家分享一个毕业设计项目作品【SSM新闻发布管理系统】 师兄根据实现的难度和等级对项目进行评分(最低0分&#xff0c;满分5分) 难度系数…

三极管在数字电路中的应用

一、认识三极管 三极管拥有3个引脚&#xff0c;分别对应3个级&#xff1a;基极(Base)、发射极&#xff08;Emitter&#xff09;、集电极(Collector)&#xff0c;如下图所示&#xff1b;下图横向左侧的是基极&#xff0c;带箭头的那个引脚就是发射极&#xff0c;另一个就是集电…

【PUSDN】java中easyexcel导入导出带有图片的Excel(main方法方式)

简述 java中easyexcel导入导出带有图片的Excel&#xff08;main方法方式&#xff09;&#xff0c;web方式详见另一篇 由于电脑音频问题&#xff0c;视频暂时没有解说声音&#xff0c; 回头重新补上 前情提示 如果有任何疑问、需求、技术支持&#xff0c;欢迎点赞&#xff0…

JavaWeb-XML

1.常见的配置文件 1.1 properties 数据库的连接就使用properties文件作为配置文件&#xff0c;properties文件中的配置信息是以键值对的形式存储的。 beiluo.jdbc.urljdbc:mysql://localhost:3306/beiluo beiluo.jdbc.drivercom.mysql.cj.jdbc.Driver beiluo.jdbc.usernamer…