加载中…
个人资料
赵江伟
赵江伟
  • 博客等级:
  • 博客积分:0
  • 博客访问:131,637
  • 关注人气:7
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

求子数组的最大和(数组)

(2012-09-09 13:47:22)
标签:

子数组

和的最大值

题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。

例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。

 

下面将逐步,从效率比较低的算法到效率比较高的算法,最后以时间复杂度O(n)的算法来实现求子数组的和的最大值。

1、 最简单的,时间复杂度O(n*n*n)的算法。

    将所有的(i,j)对儿都遍历一遍(i,j满足条件:0=<i<=j<=n),对于每个(i,j)对儿,计算x[i]到x[j]的和,并令其与到当前为止最大的子数组和maxsofar进行比较,若大于后者,则对maxsofar进行重新复制。伪代码如下:

              maxsofar=0

              for i=[0,n)

                    for j=[i,n)

                          sum=0;

                          for k=[i,j]

                                  sum=sum+x[k]  //sum代表x[i]到x[j]的和

                          maxsofar=max(maxsofar,sum) //max函数用来返回maxsofar和sum两者中的最大值

 

2、时间复杂度为O(n*n)的两个算法

     第一个是利用“x[i]到x[j]的和等于x[i]到x[j-1]的和加上x[j]”这一性质。代码是一种的代码稍作调整,伪代码如下:

             maxsofar=0

             for i=[0,n)

                   sum=0

                   for j=[i,n)

                         sum=sum+x[j]

                         maxsofar=max(maxsofar,sum)

      第二个是利用空间来换取时间的一个算法,它将从第一个元素开始到第j+1(j=[0,n))元素的和都存起来(存在数组cumarr中),这样每次计算x[i]到x[j]的和时,只需用cumarr[j]-cumarr[i-1]即可得到。伪代码如下:

            cumarr[-1]=0             

            for i=[0,n)

                  cumarr[i]=cumarr[i-1]+x[i]

            for i=[0,n)

                  for j=[i,n)

                        sum=cumarr[j]-cumarr[i-1]

                        maxsofar=max(maxsofar,sum)

 

3、利用这个思想(divide-and-conquer recipe):把解决一个大问题分解成解决几个子问题,然后把这几个子问题的结果整合起来,进而解决这个大问题。   

   利用递归算法。利用下面思路:x[l]到x[u]的最大子数组是下面三种情况之一(假设m=(l+u)/2):1、x[l]到x[u]的最大子数组完全出现在x[l]到x[m]之间。2、x[l]到x[u]的最大子数组完全出现在x[m]到x[u]之间。3、x[l]到x[u]之间的最大子数组是x[a]到x[b],其中a<m<b。伪代码如下:

               int maxsum(l,u)   //返回子数组和最大值的函数maxsum

                    if(l>u)

                         return 0

                    if(l==u)

                         return x[l]

                    m=(l+u)/2

                    lmax=sum=0

                    for(i=m;i>=l;i++)

                          sum=sum+x[i]

                          lmax=max(lmax,sum)

                    rmax=sum=0  

                    for(i=m+1;i<=u;i++)

                          sum=sum+x[i]

                          rmax=max(rmax,sum)

                    return max(lmax+rmax,maxsum(l,m),maxsum(m+1,u));

     时间复杂度:T(n)=2T(n/2)+O(n)   可以推得:T(n)=O(nlogn)

 

4、时间复杂度O(n),最优的一种算法。

     最简单的操作在数组上的算法是:从总左边(x[0])开始遍历整个数组,一直到最右边结束(x[n-1]),在这个过程中记录到目前为止最大的子数组和maxsofar。maxsofar初始化成0。假如我们已经找到x[0]到x[n-1]之间的最大子数组和,那么x[0]到x[i]之间的最大子数组和是怎样的呢?要么“还是x[0]到x[i-1]之间的最大子数组和”,要么是“从x[i]开始,往前几个连续的数的最大值”。

     在求从x[i]开始,往前几个连续的数的最大值时,用到如下性质:从x[i]开始往前几个连续的数的最大值maxending_i等于(maxending_i-1)+x[i]和0两者之中的最大值,即

                          maxending_i=max((manending_i-1)+x[i],0)

     下面是这个算法的伪代码:

                      maxsofar=0

                      maxendinghere=0

                      for i=[0,n)

                             maxendinghere=max(maxendinghere+x[i],0)

                             maxsofar=max(maxendinghere,maxsofar)

 

 

下面是算法4的C语言代码,可以参考:

 #include<stdio.h>
#define max(a,b) ((a)>(b)?(a):(b))   //定义计算a,b两者中最大值的宏

int maxsum(int arr[],int num)
{
        int i;
        int maxsofar=0;       //maxsofar记录到目前为止的最大值
        int maxendinghere=0;//maxendinghere记录从当前位置开始往前几个连续的数的和的最大值(或0)
        for(i=0;i<num;i++)
        {
                maxendinghere=max(maxendinghere+arr[i],0);
                maxsofar=max(maxsofar,maxendinghere);
        }
        return maxsofar;
}
int main()
{
        int a[8]={1,-2,3,10,-4,7,2,-5};
        int max=maxsum(a,8);
        printf("%d\n",max);
        return 0;
}

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有