求连续子数组最大和

问题

输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。

例如输入的数组为-2,11,-4,13,-5,-2 和最大的子数组为11,-4,13
因此输出为该子数组的和20。


解析:

本题解法多种多样,时间复杂度可以为:O(n^3),O(n^2),O(nlogn),O(n);

我重点介绍一下时间复杂度较低的两种方法

方法1

  1. /* 
  2.     时间复杂度O(logn)使用分治思想 
  3.     最大子连续数组可能出现在左部分,右部分,或者中间区域 
  4. */  
  5. int methord3(int* pArr, int strat, int end)  
  6. {  
  7.     if ( strat > end )  
  8.         return 0;  
  9.     if ( strat == end )  
  10.     {  
  11.         return pArr[strat];  
  12.     }  
  13.   
  14.     int mid = strat + ((end-strat)>>1);         
  15.     int lsumborder = pArr[mid], lsum = 0;  
  16.   
  17.     for ( int i = mid; i >= strat; i-- )  
  18.     {  
  19.         lsum += pArr[i];  
  20.         lsumborder = max(lsumborder,lsum);  
  21.     }  
  22.   
  23.     int rsumborder = pArr[mid+1], rsum = 0;  
  24.   
  25.     for ( int i = mid+1; i <= end; i++ )  
  26.     {  
  27.         rsum += pArr[i];  
  28.         rsumborder = max(rsumborder,rsum);  
  29.     }  
  30.   
  31.     int lmaxsum = methord3(pArr,strat,mid);//左部分的最大和  
  32.     int rmaxsum = methord3(pArr,mid+1,end);//右部分的最大和  
  33.           
  34.     return max(max(lmaxsum,rmaxsum),lsumborder+rsumborder); //判断3者中最大的作为返回值  
  35. }  

方法2

  1. /* 
  2.     本方法采用 动态规划 的思想,时间复杂度为O(n),空间复杂度为O(1) 
  3.     假设,我们已经前i个元素的最大子数组 
  4.     */  
  5. int methord4(int* pArr, int start, int end)  
  6. {  
  7.     if ( start > end )  
  8.     {  
  9.         return 0;  
  10.     }  
  11.     if ( start == end )  
  12.     {  
  13.         return pArr[start];  
  14.     }  
  15.   
  16.     int nStart = pArr[start];   //求每一段的子数组的和  
  17.     int nAll = pArr[start];     //用于记录当前的子数组和的最大值  
  18.     for ( int i = start+1; i <= end; i++ )  
  19.     {  
  20.         nStart = max(pArr[i],nStart+pArr[i]);  
  21.         nAll   = max(nStart,nAll);  
  22.     }  
  23.       
  24.     return nAll;  
  25. }  

变量变化情况
pArr[]-211-413-5-2
nSart-2117201513
nAll-21111202020

拓展:

根据上面的解法,我们很容易会做,求连续子数组乘积的最大值了。

如上述例子,那么最大子数组乘积应该是11440。

思路:

假设数组为a[],直接利用动态规划求解,考虑到可能存在负数的情况,我们用maxProduct来表示以当前循环位置i结尾的最大连续子串的乘积值,用minProduct表示以当前循环位置i结尾的最小的子串的乘积值,

那么状态转移方程为:
       maxProduct=max{a[i+1], maxProduct*a[i+1], minProduct*a[i+1]};
       minProduct=min{a[i+1], maxProduct*a[i+1], minProduct*a[i+1]};
       初始状态为 minProduct =  maxProduct = endProduct = a[0];

这样代码很容易写出来了

  1. int MaxProduct(int* pArr, int start, int end)  
  2. {  
  3.     if ( start > end )  
  4.         return 0;  
  5.     if ( start == end )  
  6.         return pArr[start];  
  7.   
  8.     int maxProduct,minProduct,endProduct;  
  9.     minProduct = maxProduct = endProduct = pArr[start];  
  10.   
  11.     for ( int i = start+1; i <= end; i++ )  
  12.     {  
  13.         int a = pArr[i]*maxProduct;  
  14.         int b = pArr[i]*minProduct;     //先把原最大值和最小值分别乘以pArr[i]记录下来,在进行比较  
  15.   
  16.         maxProduct = max( max(pArr[i],a), b);  
  17.         minProduct = min( min(pArr[i],a), b);  
  18.         endProduct = max( max(endProduct,maxProduct),minProduct );  
  19.     }  
  20.   
  21.     return endProduct;  
  22. }  
总结:只要把情况考虑清楚了,用动态规划写代码,是很容易的。

原文地址:点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值