求连续子数组的最大和

来源:互联网 发布:网络彩票法律法规 编辑:程序博客网 时间:2024/04/28 22:59

原链接:http://blog.csdn.net/v_JULY_v/article/details/6444021

第一节、求子数组的最大和
3.求子数组的最大和
题目描述:

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

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

分析:这个问题在各大公司面试中出现频率之频繁,被人引用次数之多,非一般面试题可与之匹敌。单凭这点,就没有理由不入选狂想曲系列中了。此题曾作为本人之前整理的微软100题中的第3题,至今反响也很大。ok,下面,咱们来一步一步分析这个题:
      1、求一个数组的最大子数组和,如此序列1, -2, 3, 10, -4, 7, 2, -5,我想最最直观也是最野蛮的办法便是,三个for循环三层遍历,求出数组中每一个子数组的和,最终求出这些子数组的最大的一个值。
记Sum[i, …, j]为数组A中第i个元素到第j个元素的和(其中0 <= i <= j < n),遍历所有可能的Sum[i, …, j],那么时间复杂度为O(N^3):

//本段代码引自编程之美
int MaxSum(int* A, int n)
{
 int maximum = -INF; 
 int sum=0;   
 for(int i = 0; i < n; i++)
 {
  for(int j = i; j < n; j++)
  {
   for(int k = i; k <= j; k++)
   {
    sum += A[k];
   }
   if(sum > maximum)
    maximum = sum;

   sum=0;   //这里要记得清零,否则的话sum最终存放的是所有子数组的和。也就是编程之美上所说的bug。多谢苍狼。
  }
 }
 return maximum;

      2、其实这个问题,在我之前上传的微软100题,答案V0.2版[第1-20题答案],便直接给出了以下O(N)的算法:

  1. //copyright@ July 2010/10/18  
  2. //updated,2011.05.25.  
  3. #include <iostream.h>  
  4.   
  5. int maxSum(int* a, int n)  
  6. {  
  7.     int sum=0;  
  8.     //其实要处理全是负数的情况,很简单,如稍后下面第3点所见,直接把这句改成:"int sum=a[0]"即可  
  9.     //也可以不改,当全是负数的情况,直接返回0,也不见得不行。  
  10.     int b=0;  
  11.       
  12.     for(int i=0; i<n; i++)  
  13.     {  
  14.         if(b<0)           //...  
  15.             b=a[i];  
  16.         else  
  17.             b+=a[i];  
  18.         if(sum<b)  
  19.             sum=b;  
  20.     }  
  21.     return sum;  
  22. }  
  23.   
  24. int main()  
  25. {  
  26.     int a[10]={1, -2, 3, 10, -4, 7, 2, -5};  
  27.     //int a[]={-1,-2,-3,-4};  //测试全是负数的用例  
  28.     cout<<maxSum(a,8)<<endl;  
  29.     return 0;  
  30. }  
  31.   
  32. /*------------------------------------- 
  33. 解释下: 
  34. 例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5, 
  35. 那么最大的子数组为3, 10, -4, 7, 2, 
  36. 因此输出为该子数组的和18。 
  37.  
  38. 所有的东西都在以下俩行, 
  39. 即: 
  40. b  :  0  1  -1  3  13   9  16  18  13   
  41. sum:  0  1   1  3  13  13  16  18  18 
  42.    
  43. 其实算法很简单,当前面的几个数,加起来后,b<0后, 
  44. 把b重新赋值,置为下一个元素,b=a[i]。 
  45. 当b>sum,则更新sum=b; 
  46. 若b<sum,则sum保持原值,不更新。。July、10/31。 
  47. ----------------------------------*/  

      3、不少朋友看到上面的答案之后,认为上述思路2的代码,没有处理全是负数的情况,当全是负数的情况时,我们可以让程序返回0,也可以让其返回最大的那个负数,下面便是前几日重写的,修改后的处理全是负数情况(返回最大的负数)的代码:

  1. //copyright@ July  
  2. //July、updated,2011.05.25。  
  3. #include <iostream.h>  
  4. #define n 4           //多定义了一个变量  
  5.   
  6. int maxsum(int a[n])    
  7. //于此处,你能看到上述思路2代码(指针)的优势  
  8. {  
  9.     int max=a[0];       //全负情况,返回最大数  
  10.     int sum=0;  
  11.     for(int j=0;j<n;j++)  
  12.     {  
  13.         if(sum>=0)     //如果加上某个元素,sum>=0的话,就加  
  14.             sum+=a[j];  
  15.         else     
  16.             sum=a[j];  //如果加上某个元素,sum<0了,就不加  
  17.         if(sum>max)  
  18.             max=sum;  
  19.     }  
  20.     return max;  
  21. }  
  22.   
  23. int main()  
  24. {  
  25.     int a[]={-1,-2,-3,-4};  
  26.     cout<<maxsum(a)<<endl;  
  27.     return 0;  
  28. }  

      4、DP解法的具体方程:@ flyinghearts:设sum[i] 为前i个元素中,包含第i个元素且和最大的连续子数组,result 为已找到的子数组中和最大的。对第i+1个元素有两种选择:做为新子数组的第一个元素、放入前面找到的子数组。
sum[i+1] = max(a[i+1], sum[i] + a[i+1])
result = max(result, sum[i])
 

扩展:
1、如果数组是二维数组,同样要你求最大子数组的和列?
2、如果是要你求子数组的最大乘积列?
3、如果同时要求输出子段的开始和结束列?

 

第二节、Data structures and Algorithm analysis in C

下面给出《Data structures and Algorithm analysis in C》中4种实现。

  1. //感谢网友firo  
  2. //July、2010.06.05。  
  3.   
  4. //Algorithm 1:时间效率为O(n*n*n)  
  5. int MaxSubsequenceSum1(const int A[],int N)  
  6. {  
  7.     int ThisSum=0 ,MaxSum=0,i,j,k;  
  8.     for(i=0;i<N;i++)  
  9.         for(j=i;j<N;j++)  
  10.         {  
  11.             ThisSum=0;  
  12.             for(k=i;k<j;k++)  
  13.                 ThisSum+=A[k];  
  14.               
  15.             if(ThisSum>MaxSum)  
  16.                 MaxSum=ThisSum;  
  17.         }  
  18.         return MaxSum;  
  19. }  
  20.   
  21. //Algorithm 2:时间效率为O(n*n)  
  22. int MaxSubsequenceSum2(const int A[],int N)  
  23. {  
  24.     int ThisSum=0,MaxSum=0,i,j,k;  
  25.     for(i=0;i<N;i++)  
  26.     {  
  27.         ThisSum=0;  
  28.         for(j=i;j<N;j++)  
  29.         {  
  30.             ThisSum+=A[j];  
  31.             if(ThisSum>MaxSum)  
  32.                 MaxSum=ThisSum;  
  33.         }  
  34.     }  
  35.     return MaxSum;  
  36. }  
  37.   
  38. //Algorithm 3:时间效率为O(n*log n)  
  39. //算法3的主要思想:采用二分策略,将序列分成左右两份。  
  40. //那么最长子序列有三种可能出现的情况,即  
  41. //【1】只出现在左部分.  
  42. //【2】只出现在右部分。  
  43. //【3】出现在中间,同时涉及到左右两部分。  
  44. //分情况讨论之。  
  45. static int MaxSubSum(const int A[],int Left,int Right)  
  46. {  
  47.     int MaxLeftSum,MaxRightSum;              //左、右部分最大连续子序列值。对应情况【1】、【2】  
  48.     int MaxLeftBorderSum,MaxRightBorderSum;  //从中间分别到左右两侧的最大连续子序列值,对应case【3】。  
  49.     int LeftBorderSum,RightBorderSum;  
  50.     int Center,i;  
  51.     if(Left == Right)Base Case  
  52.         if(A[Left]>0)  
  53.             return A[Left];  
  54.         else  
  55.             return 0;  
  56.         Center=(Left+Right)/2;  
  57.         MaxLeftSum=MaxSubSum(A,Left,Center);  
  58.         MaxRightSum=MaxSubSum(A,Center+1,Right);  
  59.         MaxLeftBorderSum=0;  
  60.         LeftBorderSum=0;  
  61.         for(i=Center;i>=Left;i--)  
  62.         {  
  63.             LeftBorderSum+=A[i];  
  64.             if(LeftBorderSum>MaxLeftBorderSum)  
  65.                 MaxLeftBorderSum=LeftBorderSum;  
  66.         }  
  67.         MaxRightBorderSum=0;  
  68.         RightBorderSum=0;  
  69.         for(i=Center+1;i<=Right;i++)  
  70.         {  
  71.             RightBorderSum+=A[i];  
  72.             if(RightBorderSum>MaxRightBorderSum)  
  73.                 MaxRightBorderSum=RightBorderSum;  
  74.         }  
  75.         int max1=MaxLeftSum>MaxRightSum?MaxLeftSum:MaxRightSum;  
  76.         int max2=MaxLeftBorderSum+MaxRightBorderSum;  
  77.         return max1>max2?max1:max2;  
  78. }  
  79.   
  80. //Algorithm 4:时间效率为O(n)  
  81. //同上述第一节中的思路3、和4。  
  82. int MaxSubsequenceSum(const int A[],int N)  
  83. {  
  84.     int ThisSum,MaxSum,j;  
  85.     ThisSum=MaxSum=0;  
  86.     for(j=0;j<N;j++)  
  87.     {  
  88.         ThisSum+=A[j];  
  89.         if(ThisSum>MaxSum)  
  90.             MaxSum=ThisSum;  
  91.         else if(ThisSum<0)  
  92.             ThisSum=0;  
  93.     }  
  94.     return MaxSum;  
  95. }   

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 怀孕8个月感冒鼻窦炎头疼怎么办 买的全身镜下面的框子坏了怎么办 单位上司姐姐每天要接我上班怎么办 企业微信公众号中的文章边框怎么办 怎么办我在数学答题卡上画了分割线 游泳的时候泳裙飘起来怎么办 两岁宝宝误服了酵素梅怎么办 人被困在山洞里没有氧气怎么办? 一个人太爱你可你不爱他怎么办 牙齿还没掉又长了新牙齿怎么办 两岁宝宝牙齿发黑烂牙怎么办 怀孕八个月被小孩压到肚子了怎么办 在花场上班客人约我出去玩怎么办 小天才电话手表开不了机怎么办 黑色笔芯弄在白色衣服上怎么办 su文件打开是意外的格式怎么办 su卡的动一下就卡怎么办 犀牛vary渲染的太曝光了怎么办 脑子老是乱想幻想控制不住怎么办 猫抓了破了点皮怎么办 小孩子喜欢玩别人家的玩具怎么办 孩子把别人家的玩具玩坏了怎么办 小新和爸妈一起睡觉那他们怎么办 ps画纸画的时候一直在移动怎么办 宝宝磕碰到家具上鼻梁碰破该怎么办 月经推迟11天了怎么办孕测棒单杠 小孩子不小心把蜡笔吃一点怎么办 线切割切割的工件表面有条纹怎么办 苹果手机不能用流量更新吃鸡怎么办 吃鸡更新成雨林如果内存不够怎么办 绝地求生刺激战场背包满了怎么办 绝地求生刺激战场模拟器满了怎么办 绝地求生刺激战场电脑版满了怎么办 绝地求生手游模拟器注册上限怎么办 绝地求生电脑模拟器已达上限怎么办 三星s5锁屏密码忘了怎么办 字画装框的时候起褶皱怎么办 指甲油抹在手上不在指甲上怎么办 我把油画的布割破了怎么办 胜战本领怎么看、走上战场怎么办 胜战本领怎么看 走上战场怎么办