0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

动态规划:8行代码搞定最大子数组和问题

算法与数据结构 来源:码农的荒岛求生 作者:码农的荒岛求生 2022-04-01 10:24 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

铁子们,我是小风哥,你也可以叫我岛主

今天给大家带来一道极其经典的题目,叫做最大和子数组,给定一个数组,找到其中的一个连续子数组,其和最大。

示例:

输入:nums=[-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:子数组[4,-1,2,1]的和为6,其它任何连续的子数组和不超过6.

想一想该怎样解决这个问题。

如果你一时想不到解法可以从暴利解法开始。

暴力求解

这种解法最简单,我们把所有子数组找出来,然后依次计算其和,找出一个最大的出来,比如给定数组[1,2,3],那么我们能找出子数组:[1],[2],[3],[1,2],[2,3],[1,2,3],很显然这里和最大的子数组为[1,2,3],其值为6。

intsum(vector&nums,intb,inte){
intres=0;
for(;b<= e; b++) {
        res += nums[b];
    }
    returnres;
}
intmaxSubArray(vector&nums){
intsize=nums.size();
intres=0x80000000;
for(inti=0;i< size; i++) {
        for(intj=i;j< size; j++) {
            res = max(res, sum(nums, i, j));
        }
    }
    returnres;
}

这种解法最简单,该算法的时间复杂度为O(n^3),其中找出所有子数组的时间复杂度为O(n^2),计算每个子数组的和的时间复杂度为O(n),因此其时间复杂度为O(n^3)。

让我们再来看一下这个过程,这里的问题在于计算每个子数组的和时有很多重复计算,比如我们知道了子数组[1,2]的和后再计算数组[1,2,3]的值时完全可以利用子数组[1,2]的计算结果而无需从头到尾再算一遍,也就是说我们可以利用上一步的计算结果,这本身就是动态规划的思想。

8589ba58-b15c-11ec-aa7f-dac502259ad0.png

基于该思想我们可以对上述代码简单改造一下:

intmaxSubArray(vector&nums){
intsize=nums.size();
intres=0x80000000;
for(inti=0;i< size; i++) {
        int sumer = nums[i];
        res = max(res, sumer);
        for(intj=i+1;j< size; j++) {
            sumer += nums[j];
            res = max(res, sumer);
        }
    }
    returnres;
}

看到了吧,代码不但更简洁,而且运行速度更快,该算法的时间复杂度为O(n^2),比第一种解法高了很多。

还有没有进一步提高的空间呢?

答案是肯定的。

分而治之

我们在之前的文章中说过,分治也是一种非常强大的思想,具体应该这里的话我们可以把整个数组一分为二,然后子数组也一分为二,不断划分下去就像这样:

859b978c-b15c-11ec-aa7f-dac502259ad0.png

然后呢?

然后问题才真正开始有趣起来,注意,当我们划分到最下层的时候,也就是不可再划分时会得到两个数组元素,比如对于数组[1,2]会划分出[1]与[2],此时[1]与[2]不可再划分,那么对于子问题[1,2],其最大子数组的和为max(1+2, 1,2),也就是说要么是左半部分的元素值、要么是右半部分的元素值、要么是两个元素的和,就这样我们得到了最后两层的答案:

85bb2606-b15c-11ec-aa7f-dac502259ad0.png

假设对于数组[1,2,3,4],一次划分后得到了[1,2]与[3,4],用上面的方法我们可以分别知道这两个问题的最大子数组和,我们怎样利用上述的答案来解决更大的问题,也就是[1,2,3,4]呢?

很显然,对于[1,2,3,4]来说,最大子数组的和要么来自左半部分、要么来自右半部分、要么来自中间部分——也就是包含2和3,其中左半部分和右半部分的答案我们有了,那么中间部分的最大和该是多少呢?

其实这个问题很简单,我们从中间开始往两边不断累加,然后记下这个过程的最大值,比如对于[1,-2,3,-4,5],我们从中间的3开始先往左边累加和是:{1+(-2)+3, (-2)+3, 3}也就是{2,1,3},因此我们以中间数字为结尾的最大子数组和为3:

85ce2bde-b15c-11ec-aa7f-dac502259ad0.png

另一边也是同样的道理,只不过这次是以中间数字为起点向右累加:

85e0d090-b15c-11ec-aa7f-dac502259ad0.png

然后这三种情况中取一个最大值即可,这样我们就基于子问题解决了更大的问题:

85f977a8-b15c-11ec-aa7f-dac502259ad0.png

此后的道理一样,最终我们得到了整个问题的解。

根据上面的分析就可以写代码了:

intgetMaxSum(vector&nums,intb,inte){
if(b==e)returnnums[b];
if(b==e-1)returnmax(nums[b],max(nums[e],nums[b]+nums[e]));
intm=(b+e)/2;
intmaxleft=nums[m];
intmaxright=nums[m];
intsum=nums[m];

for(inti=m+1;i<= e; i++) {
        sum += nums[i];
        maxright = max(maxright, sum);
    }

    sum = nums[m];
    for(inti=m-1;i>=b;i--){
sum+=nums[i];
maxleft=max(maxleft,sum);
}
returnmax(getMaxSum(nums,b,m-1),max(getMaxSum(nums,m+1,e),maxleft+maxright-nums[m]));
}
intmaxSubArray(vector&nums){
returngetMaxSum(nums,0,nums.size()-1);
}

上述这段代码的时间复杂度为O(NlogN)比第二种方法又提高了很多。

动态规划

实际上这个问题还有另一种更妙的解决方法,我们令dp(i)表示以元素A[i]为结尾的最大子数组的和,那么根据这一定义则有:

860dadd6-b15c-11ec-aa7f-dac502259ad0.png

这是很显然的,注意dp(i)的定义,是以元素A[i]为结尾的最大子数组的和,因此dp(i)的值要么就是A[i]连接上之前的一个子数组,那么不链接任何数组,那么最终的结果一定是以某个元素为结尾的子数组,因此我们从所有的dp(i)中取一个最大的就好了,依赖子问题解决当前问题的解就是所谓的动态规划。

有了这些分析,代码非常简单:

intmaxSubArray(vector&nums){
intsize=nums.size();
vectordp(size,0);
intres=dp[0]=nums[0];
for(inti=1;i< size; i++) {
        dp[i] = max(dp[i - 1] + nums[i], nums[i]);
        res = max(res, dp[i]);
    }
    returnres;
}

这段代码简单到让人难以置信,只有8行代码,你甚至可能会怀疑这段代码的正确性,但它的确是没有任何问题的,而且这段代码的时间复杂度只有O(N),这段代码既简单运行速度又快,这大概就是算法的魅力吧。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4985

    浏览量

    74632
  • 数组
    +关注

    关注

    1

    文章

    420

    浏览量

    27548

原文标题:动态规划:8行代码搞定最大子数组和问题

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    EN18031 认证全解析:三大子标准搞定欧盟网络安全合规

    2026年欧盟网络安全监管持续收紧,EN18031作为RED指令强制网络安全标准,已成为无线设备出海欧盟的核心门槛。该认证由三大子标准构成,分别对应不同安全场景,精准匹配即可高效完成合规。本文全面
    的头像 发表于 02-06 15:41 2323次阅读
    EN18031 认证全解析:三<b class='flag-5'>大子</b>标准<b class='flag-5'>搞定</b>欧盟网络安全合规

    不懂编程,怎么搞定电子仪表上位机软件?零代码搞定上位机软件开发

    “不懂编程,怎么搞定电子仪表上位机软件?”这是很多电子仪表用户的共同困惑。传统上位机开发被“专业编程”门槛牢牢限制,即便你对测试需求了如指掌(比如知道要采集哪些仪表数据、怎么分析波形、怎么生成
    的头像 发表于 01-27 17:19 869次阅读
    不懂编程,怎么<b class='flag-5'>搞定</b>电子仪表上位机软件?零<b class='flag-5'>代码</b><b class='flag-5'>搞定</b>上位机软件开发

    RT-Thread Vector软件包:嵌入式开发的动态数组容器 | 技术集结

    RT-Thread Vector软件包:嵌入式开发的动态数组容器 | 技术集结
    的头像 发表于 01-25 09:33 5776次阅读
    RT-Thread Vector软件包:嵌入式开发的<b class='flag-5'>动态</b><b class='flag-5'>数组</b>容器 | 技术集结

    keil中c语言的动态分配内存

    )包含柔性数组成员的结构体要用malloc函数进行动态内存分配,并且分配的内存应该大于该结构体的大小以适应柔性数组的预期大小。看下面的代码: 其实上面的
    发表于 01-21 06:04

    深开鸿开源鸿蒙社区主干代码贡献量破650万

    历经五年发展,开源鸿蒙已从技术萌芽成长为万物智联时代的核心数字底座。秉持开源、共建、共享、共荣的理念,其生态规模持续扩张,累计汇聚超10000名贡献者、510多家合作伙伴,沉淀1.3亿代码
    的头像 发表于 01-07 10:22 809次阅读

    数组的初体验

    程序中也需要容器,只不过该容器有点特殊,它在程序中是一块连续的,大小固定并且里面的数据类型一致的内存空间,它还有个好听的名字叫数组。可以将数组理解为大小固定,所放物品为同类的一个购物袋,在该购 物
    发表于 11-25 08:06

    二维数组介绍

    大家不要认为二维数组在内存中就是按、列这样二维存储的,实际上,不管二维、三维数组… 都是编译器的语法糖。 存储上和一维数组没有本质区别,举个例子: int array[3][3
    发表于 11-25 07:42

    基于感知引导的多步骤精细操作任务与运动规划

    传统的任务与运动规划(TAMP)系统在机器人操作应用中通常依赖静态模型运行,因此在面对新环境时往往表现不佳。将感知与操作相融合,是应对这一挑战的有效途径,使机器人能够在执行过程中实时更新规划,从而适应动态变化的场景。
    的头像 发表于 11-14 10:18 1756次阅读
    基于感知引导的多步骤精细操作任务与运动<b class='flag-5'>规划</b>

    基于SIMP与折衷规划法的航空附件齿轮箱结构轻量化设计与动态特性提升

    航空发动机附件齿轮箱作为动力传递系统的关键部件,其箱体结构设计直接影响发动机的功率密度、可靠性及振动特性。针对传统经验设计方法难以满足高刚度、轻量化及高动态性能要求的挑战,本文提出了一种基于折衷规划法的多目标拓扑优化方法。
    的头像 发表于 11-07 15:21 1182次阅读
    基于SIMP与折衷<b class='flag-5'>规划</b>法的航空附件齿轮箱结构轻量化设计与<b class='flag-5'>动态</b>特性提升

    加载动态模块报错,提示memset函数未找到,但是代码没用到memset,为什么?

    动态模块提示memset未找到,但是代码没用到memset。 报错: Module: can’t find memset in kernel symbol table
    发表于 10-14 06:59

    商品价格动态调整接口技术详解

    ​  在电商或零售系统中,商品价格需根据市场动态(如供需变化、竞争环境)实时调整,以最大化利润和竞争力。本文将从接口设计、核心算法、实现代码到优化策略,逐步解析如何构建一个高效的“商品价格动态
    的头像 发表于 10-13 15:49 666次阅读
    商品价格<b class='flag-5'>动态</b>调整接口技术详解

    虹科动态 | 2025年8月精彩回顾

    」...下面让我们一起回顾8月的虹科动态吧,9月精彩活动预告也不容错过!01虹科动态1品牌动态8月7日,香港工业总会在港举办第65届周年大会
    的头像 发表于 09-02 10:13 1105次阅读
    虹科<b class='flag-5'>动态</b> | 2025年<b class='flag-5'>8</b>月精彩回顾

    华砺智成立八周年

    以落地中国为起点,2017年8月,华砺智砥砺八载春秋,从几行通信代码到覆盖三大洲的智慧路网,用2920个昼夜完成了一场关于坚持的叙事,淬炼出坚固、立体、创新的多面形态。
    的头像 发表于 08-20 14:51 1077次阅读

    使用PSoC4_WriteRow写入闪存时出现问题,“数据大小不等于给定数组大小”,为什么?

    我正在使用CY8C4025AXI-S412芯片,并尝试使用 PSoC™编程器 CLI 工具,特别是 PSoC4_WriteRow 命令。 根据数据表和 PSoC4_GetFlashInfo ,闪存
    发表于 07-18 07:00

    AGV小车中的动态路径规划算法揭秘

    并非一成不变时,动态路径规划能力就显得至关重要。本文将深入探讨几种主流的动态路径规划算法(如A、Dijkstra、RRT等),并解析它们如何在AGV行业中大显身手。 为何需要
    的头像 发表于 06-17 15:54 2195次阅读
    AGV小车中的<b class='flag-5'>动态</b>路径<b class='flag-5'>规划</b>算法揭秘