基本算法-求最大子数组和 及其变种

系统 1696 0

  这是个非常常见的算法题,见诸于《编程之美》、《编程珠玑》等经典算法书籍(亦或,经典面试书籍:))。网上有很多关于这个问题的讨论和实现,我谨在此写下自己的理解,可能之前有人写过,但毕竟是自己思考出来的东西,权当记录一下。

  问题:一个有N个整数元素的一维数组(A[0],A[1].....,A[n-1]),这个数组当然有很多个子数组(n*n个),求最大的子数组之和。

  经典解法:

      
        1
      
       maxsofar=
      
        0
      
      
        2
      
         maxendinghere=
      
        0
      
      
        3
      
      
        for
      
       i=[
      
        0
      
      
        ,n)

      
      
        4
      
           maxendinghere=max(maxendinghere+a[i],
      
        0
      
      
        )

      
      
        5
      
           maxsofar=max(maxendinghere,maxsofar)
    

  在此我写的是《编程珠玑》上的伪代码,我认为这个伪代码以及Bentley的解释最好的诠释了这个问题。

  Bentley写到: The key to understanding this program is the variable maxendinghere.这个算法是典型的动态规划算法,表述如下:

  我们在计算前i个数的最大子数组和时,先记录前i-1个数的最大子数组之和Max i-1 。当我们遇到新的数A[i]时,如果A[i]的加入会使得某个子数组的和大于前i-1个数的最大子数组和,则更新Max i =该子数组的和,否则,Max i =Max i-1 .接下来我们只需要考虑,A[i]的加入可能会使哪个子数组的和增加?当然是以i-1个数结尾的子数组。所以,在计算前i个数的最大子数组之和时,我们除了要记录前i-1个数的最大子数组之和,还应该记录以i-1个数结尾的最大子数组之和。算法如下:

  假设我们已经知道了前i-1个数组的最大子数组为A[j],....,A[k],其和为Sum k ,同时我们记录以i-1为结尾的最大子数组为A[m],...,A[i-1],其和为Sum i-1 ,显然,Sum i-1 <=Sum k

  当遇到第i个数时,如果A[i]>0,则Sum i-1 +A[i]可能会大于Sum k .所以我们更新前i个数的最大子数组之和为:max{Sum i-1 +A[i],Sum k }.

  然后我们还应该更新以i结尾的最大子数组之和:Sum i =max{Sum i-1 +A[i],0}.亦即,如果Sum i-1 +A[i]大于0,则以i结尾的最大子数组为A[m],...,A[i-1],A[i]。否则,若Sum i-1 +A[i]小于等于0,则该子数组的和还不如一个空的子数组的和0,将该子数字重置为空,其和为0.


  这个经典问题有一个很类似的变种:即同样给定一个数组,写一个在其中找出不连续子数组和的最大值,也就是说子数组里的任意相邻的两个元素,在原数组里都必须是不相邻的才行。同样以数组{1, -2, 3, 5, -1, 2}为例,则和最大的不连续子数组是{1, 5, 2},函数返回值是8。

  有了第一个问题的解答,第二个问题应该很简单。我们考虑下,假设我们已经知道了前i-1个数的数组的不连续子数组和的最大值,当第i个数加入时,这个不连续子数组和在什么情况下会改变?

  第i个数加入后不能与以i-1结尾的最大不连续子数组连接,这样会违反不连续的规则。所以我们要考虑结尾最大为i-2的和最大的不连续子数组,设其和为Sum i-2 .当Sum i-2 +A[i]大于以第i-1个数结尾的不连续子数组和的最大值时,我们更新前i个数组成的数组的不连续子数组和的最大值为Sum i-2 +A[i]。算法的代码为:

      
        1
      
      
        for
      
      (
      
        int
      
       i=2;i<length;i++
      
        ){            

      
      
        2
      
           maxendingi=max(maxendingi_2+
      
        array[i],maxendingi_2,maxendingi_1);

      
      
        3
      
      
        int
      
       tmp=
      
        maxendingi_2;

      
      
        4
      
           maxendingi_2=
      
        max(maxendingi_1,maxendingi_2);

      
      
        5
      
      
        maxendingi_1=max(tmp+array[i],array[i]);

      
      
        6
      
       }
    

   其中maxendingi表示前i个数组成的数组的最大不连续子数组的和。

  maxendingi_2记录的是结尾最大为i-2的和最大的不连续子数组的和,初始化为array[1]。

  maxendingi_1记录的是以i-1结尾的最大的不连续子数组的和,初始化为array[2]。

  数组长度小于3的情况可以另行处理。

参考文献:

  [1] Jon Bentley 编程珠玑 p81

  [2] 面试趣题:求不连续子数组最大和的值

基本算法-求最大子数组和 及其变种


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论