最大子序列和问题

最大子序列和问题

  1. 对于最大子序列求和问题有很多种解法。其中比较简单的是使用3层嵌套for循环进行求解,这种方法非常简单但很暴力,需要 O ( N 3 ) O(N^{3}) O(N3)的时间花费。
int MaxSubsequenceSum( const int A[], int N)
{
   int  ThisSum, MaxSum, i, j, k;
   MaxSum = 0;
   for(i=0; i<N; i++)
     for(j=i; j<N; j++)
     {
        ThisSum = 0;
        for(k=i; k<j; k++)
           ThisSum += A[k];  //计算从i到j之间的和
        if(ThisSum>MaxSum)
           MaxSum = ThisSum;
     }
     return MaxSum;                 
}
  1. 我们稍微优化一下可以变成一个2层嵌套for循环,但实质上并没有简化多少,时间复杂度 O ( N 2 ) O(N^{2}) O(N2)
int MaxSubsequenceSum( const int A[], int N)
{
  int  ThisSum, MaxSum, i, j;
  MaxSum = 0;
  for(i=0; i<N; i++)
    ThisSum = 0;
    for(j=i; j<N; j++)
    {
       ThisSum += A[j];  //计算每个从i为起点的最大值
       if(ThisSum>MaxSum)
          MaxSum = ThisSum;
    }
    return MaxSum;                
}
  1. 再高端一些,我们可以使用分治策略,递归的计算序列左半部、右半部的最大子序列和,比较序列左半部、右半部以及中间的最大和进行求解。
//递归函数
static int MaxSubSum( const int A[], int Left, int Right)
{
 int  MaxLeftSum, MaxRightSum;
 int  MaxLeftBorderSum, MaxRightBorderSum;
 int  LeftBorderSum ,RightBorderSum;
 int  Center ,i;
 //递归停止条件
 if(Left==Right)
     if(A[Left]>0)
       return A[Left];
     else
       return 0;
 //计算中间位置
 Center = ( Left + Right ) / 2;
 //递归求解左半部和右半部最大和
 MaxLeftSum = MaxSubSum(A, Left, Center);        
 MaxRightSum = MaxSubSum(A, Center+1, Right);
 //计算左边界最大值
 MaxLeftBorderSum = 0;
 LeftBorderSum = 0;
 for(i=Center; i>=Left; i--)
 {
   LeftBorderSum += A[i];
   if(LeftBorderSum>MaxLeftBorderSum)
      MaxLeftBorderSum = LeftBorderSum;
 }
 //计算右边界最大值
 MaxRightBorderSum = 0;
 RightBorderSum = 0;
 for(i=Center; i<=Right; i++)
 {
   RightBorderSum += A[i];
   if(RightBorderSum>MaxRightBorderSum)
      MaxRightBorderSum = RightBorderSum;
 }
 //比较左半部、右半部以及中间边界的最大值
 return Max3(MaxLeftSum, MaxRightSum,   
             MaxLeftBorderSum+MaxRightBorderSum);
}
//开始递归函数
int MaxSubsequenceSum(const int A[], int N)
{
   return  MaxSubSum(A, 0, N-1); 
}

       我们可以知道:
T ( 1 ) = 1 T(1)=1 T(1)=1
T ( N ) = 2 T ( N / 2 ) + O ( N ) T(N)=2T(N/2)+O(N) T(N)=2T(N/2)+O(N)
       通过递推方法我们可以得到:
若 N = 2 k , T ( N ) = N ∗ ( k + 1 ) + N = N l o g N + N 若N=2^k,T(N)=N*(k+1)+N=NlogN+N N=2kT(N)=N(k+1)+N=NlogN+N
       因此该算法时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)

  1. 上诉三种方法都比较好理解,下面介绍一种最方便的求解方法。
int MaxSubsequenceSum( const int A[], int N)
{
   int  ThisSum, MaxSum, i;
   MaxSum = 0;
   for(i=0; i<N; i++)
   {
      ThisSum += A[i];
      if(ThisSum>MaxSum)
         MaxSum = ThisSum;
      else if(ThisSum<0)
         ThisSum = 0;       
   }
   return MaxSum;                
}

       这个方法看似很难理解,其实我们想清楚后是很明朗的。
       首先代码表示的是如果当前数之前的序列求和大于MaxSum则交换,若求和小于零,那么本次求和归零,从下一个点从新开始求和。
       可以这么想,假如从序列A第一个数开始加,加到第i个数时求和为负数,可以肯定的是A[i]一定为负数,那我们从A[i]开始反过去求和会发现求和即便在变大它也一定是负的,因此这段序列对之后的求和完全没有正作用,因此我们要清零,从新开始计算下一组序列。
       该算法的时间复杂度为 O ( N ) O(N) O(N),并且该算法只需要对A[]进行一次扫描,并且不需要被记忆。

       其实该算法与动态规划相似,动态规划使用递推关系来计算:
d p [ i ] = m a x ( 0 , d p [ i − 1 ] ) + A [ i ] dp[i]=max(0, dp[i-1])+A[i] dp[i]=max(0,dp[i1])+A[i]
        其中 d p [ i ] dp[i] dp[i]表示以第i个数结尾的最大子序列和,同样的若前一个数的最大子序列和大于0则继续求和,否则从当前点重新开始求和。

       本次算法都来自于数据结构与算法分析,c语言描述版。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值