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

    文章

    4557

    浏览量

    66835
  • 数组
    +关注

    关注

    1

    文章

    409

    浏览量

    25597

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

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

收藏 人收藏

    评论

    相关推荐

    PHP中数组的使用方法!

    PHP中数组的使用方法! PHP是一种广泛使用的网络编程语言,它的数组功能非常强大且灵活。数组是一种数据结构,它允许我们在单个变量中存储多个值。 在本篇文章中,我将详细解释PHP数组
    的头像 发表于 01-12 15:11 168次阅读

    js中如何判断数组中包含某个特定的值

    不存在则返回-1。通过检查返回的索引值是否大于等于0,我们可以确定数组中是否包含该元素。以下是示例代码: let array = [ 1 , 2 , 3 , 4 , 5 ]; let value
    的头像 发表于 11-30 16:21 357次阅读

    jsp判断数组是否包含某个值

    JSP(JavaServerPages)是一种能够使用Java开发动态网页的技术。在本文中,我们将探讨有效地确定数组是否包含JSP中特定值的技术和方法。这个过程包括理解数组的基本结构,访问和操作
    的头像 发表于 11-30 16:18 463次阅读

    将一维数组转为二维python

    将一维数组转为二维数组是一个常见的问题,特别是在处理数据时。一维数组是由一个连续的数据块组成,而二维数组则是由多个一维数组组成的
    的头像 发表于 11-23 14:54 2271次阅读

    python列表和数组的区别

    ,从内部实现、性能、操作方式等多个方面进行详细分析,以帮助读者更好地理解它们之间的区别和适用场景。 一、内部实现: 列表(List): Python中的列表是一种有序的、可变的、可包含不同类型元素的数据结构。列表的实现基于动态数组,可以
    的头像 发表于 11-21 15:13 880次阅读

    python如何定义二维空数组

    行和列的数量,并将它们初始化为0。然后,可以使用一个for循环来逐行创建一个空的一维列表,并使用另一个for循环来向空的二维列表中添加这些一维列表。 下面是一个示例代码,创建一个3行4列的空二维数组
    的头像 发表于 11-21 15:12 690次阅读

    西门子博途中如何读取其它类型数组最大值及索引

    此程序可以求其它类型数组最大值及索引,只要在FC中再添加一些程序即可。
    的头像 发表于 11-10 09:29 733次阅读
    西门子博途中如何读取其它类型<b class='flag-5'>数组</b>的<b class='flag-5'>最大</b>值及索引

    什么是数组数组有什么用?数组的使用方法

    数组(Array)是有序的元素序列。
    的头像 发表于 11-08 14:58 710次阅读
    什么是<b class='flag-5'>数组</b>?<b class='flag-5'>数组</b>有什么用?<b class='flag-5'>数组</b>的使用方法

    多轴伺服,一芯搞定

    多轴伺服,一芯搞定
    的头像 发表于 10-19 17:54 340次阅读
    多轴伺服,一芯<b class='flag-5'>搞定</b>

    数组的定义 什么是数组

    数组 数组是内置类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。 在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。
    的头像 发表于 10-09 09:39 1355次阅读

    C++数组名和数组拷贝详解

    C++数组间赋值不能直接通过数组名称 randy = sesame进行,因为数组名并不是指针,大部分情况下,编译器会隐式转换为指向数组首元素的指针常量。
    发表于 08-21 15:09 302次阅读
    C++<b class='flag-5'>数组</b>名和<b class='flag-5'>数组</b>拷贝详解

    C语言中指针数组数组指针的区别

    指针和数组之间存在着紧密的关系。在本文中,我们将探讨指针和数组的关系、指针算术和数组遍历、多维数组与指针以及指针数组
    发表于 08-17 15:29 281次阅读

    动态数组和C++ std::vector详解

    std::vector是C++的默认动态数组,其与array最大的区别在于vector的数组动态的,即其大小可以在运行时更改。std::v
    的头像 发表于 07-19 11:07 719次阅读

    C 语言数组的基本结构

    的元素 求数组中元素的最短距离 求两个有序数组的共同元素 求三个数组的共同元素 找出数组中唯一的重复元素 找出出现奇数次的元素 求数组中满足
    的头像 发表于 06-22 10:56 387次阅读

    带你了解SystemVerilog中的关联数组

    在SystemVerilog中,我们知道可以使用动态数组实现数组元素个数的动态分配,即随用随分
    的头像 发表于 06-09 09:46 4750次阅读
    带你了解SystemVerilog中的关联<b class='flag-5'>数组</b>