数据结构学习笔记-Day1-最大子列和问题

最大子列和问题

给定N个整数的序列{A1A2A3…,AN},求函数的最大值.


最直接最暴力的算法:把所有的子列和都算出来,找出最大的那一个.

int MaxSubseqSum1( int A[], int N )  
{   int ThisSum, MaxSum = 0;
    int i, j, k;
    for( i = 0; i < N; i++ ) { /* i是子列左端位置 */
          for( j = i; j < N; j++ ) { /* j是子列右端位置 */
                  ThisSum = 0;  /* ThisSum是从A[i]到A[j]的子列和 */
                  for( k = i; k <= j; k++ )
                            ThisSum += A[k];
                            if( ThisSum > MaxSum ) /* 如果刚得到的这个子列和更大 */
                                      MaxSum = ThisSum;    /* 则更新结果 */
          } /* j循环结束 */
     } /* i循环结束 */
     return MaxSum;  
}

这个算法非常的不聪明.

当我们知道一个从ij的和的时候,我们计算下一个从ij,并不需要从头开始加.j增加了1的时候,我们只需要在前面的ij的部分和后面加一个元素就好了,k循环是多余的.

算法2

int MaxSubseqSum2( int A[], int N )  
{   int ThisSum, MaxSum = 0;
   int i, j;
    for( i = 0; i < N; i++ ) { /* i是子列左端位置 */
          ThisSum = 0;  /* ThisSum是从A[i]到A[j]的子列和 */
          for( j = i; j < N; j++ ) { /* j是子列右端位置 */
                  ThisSum += A[j];        /*对于相同的i,不同的j,只要在j-1次循环的基础上累加1项即可*/ 
                  if( ThisSum > MaxSum ) /* 如果刚得到的这个子列和更大 */
                            MaxSum = ThisSum;    /* 则更新结果 */
          } /* j循环结束 */    
     } /* i循环结束 */    
     return MaxSum;  
}

这个算法的复杂度为T(N)=O(N2).两重循环的嵌套.


但这个算法并不是最好的.

 当我们发现一个算法的复杂度为O(N2)时,我们应该下意识的想到能否将它改进为O(NlogN).


算法3:分而治之

把一个大的问题切分成很多块,再分头去解决每个小的问题,最后再把结果合并起来

 

分:把这个数组从中间一分为二

治:然后递归地去解决左右两边的问题,得到左边的最大子列和和右边的最大子列和以及跨越边界的最大子列和,比较三者最大值即为所求的最大子数列

int MaxSubSeqSum(int arr[], int left, int right) {
    if(left == right){
        if(arr[left] > 0){
            return arr[left];
        }else {
            return 0;
        }
    }

    int center = (left + right) / 2;
    int leftMaxSum = MaxSubSeqSum(arr, left, center); /* 分界线左侧最大子数列 */
    int rigthMaxSum = MaxSubSeqSum(arr, center+1, right); /* 分界线右侧最大子数列 */

    /* 以分界线往左求最大子数列 */
    int leftBorderSum = 0, maxLeftBorderSum = 0;
    for(int i=center; i>=left; i--) {
        leftBorderSum += arr[i];
        if(leftBorderSum > maxLeftBorderSum){
            maxLeftBorderSum = leftBorderSum;
        }
    }

    /* 以分界线往右求最大子数列 */
    int rightBorderSum = 0, maxRightBorderSum = 0;
    for(int j=center+1; j<=right; j++) {
        rightBorderSum += arr[j];
        if(rightBorderSum > maxRightBorderSum) {
            maxRightBorderSum = rightBorderSum;
        }
    }

    /* 跨越分界线最大子数列和 */
    int maxBorderSum = maxLeftBorderSum + maxRightBorderSum;

    return maxSum(leftMaxSum, rigthMaxSum, maxBorderSum);
}

/*
**  返回a, b, c三者最大值
*/
int maxSum(int a, int b, int c){
    if(a > b) {
        a = b;
    }
    if(a > c) {
        return a;
    }else {
        return c;
    }
}

T(N) = 2 *T(N/2) +cN                         T(1)=O(1)

= 2[2 * T(N/22) +cN/2]+cN

=2kO(1)+ck N                           其中N/2k=1

= O(NlogN)

但这仍然不是最快的算法.


算法4:在线处理(每输入一个数据就进行即时处理)

int MaxSubseqSum4( int A[], int N )  
{   int ThisSum, MaxSum;
    int i;
    ThisSum = MaxSum = 0;
    for( i = 0; i < N; i++ ) {
          ThisSum += A[i]; /* 向右累加 */
          if( ThisSum > MaxSum )
                  MaxSum = ThisSum; /* 发现更大和则更新当前结果 */
          else if( ThisSum < 0 ) /* 如果当前子列和为负 */
                  ThisSum = 0; /* 则不可能使后面的部分和增大,抛弃之 */
    }
    return MaxSum;  
}

它里面只有一个for循环,所以它的时间复杂度为T(N)=O(N),是线性的.

副作用:它的正确性不是特别的明显.




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值