最大和连续子数组

来源:互联网 发布:知乎 招聘 编辑:程序博客网 时间:2024/05/17 02:48

问题描述:给定一个数组A[0,1…n-1],求A的连续子数组,使该数组和最大

一. 暴力法 
分析:首先初始化要求的最大值maxSum为A[0],然后定义三个索引i、j、k,然后三层循环:第一层i从0遍历到n-1,第二层j从i遍历到n-1,第三层k从i遍历到j,求出A[i]到A[j]之间的元素的和,然后和maxSum作比较并更新maxSum。

复杂度:时间复杂度为O(n^3),空间复杂度O(1)

代码:

[cpp] view plain copy
  1. int MaxSubArray(int A[], int n)  
  2. {  
  3.     int maxSum = A[0];  
  4.     int currSum;  
  5.     for(int i = 0; i < n; i++)  
  6.     {  
  7.         for(int j = i; j < n; j++)  
  8.         {  
  9.             currSum = 0;  
  10.             for(int k = i; k <=j; k++)  
  11.             {  
  12.                 currSum += A[k];  
  13.             }  
  14.             if(currSum > maxSum)  
  15.                 maxSum = currSum;  
  16.         }  
  17.     }  
  18.     return maxSum;  
  19. }  

二. 暴力法优化版本 
分析:在第一个版本中,由于在第二层循环遍历过程中,如果j增加1变为j+1,k从i到j又要重复算一次,其实如果优化一下,将前面的从A[i]到[j]计算的和保留下来,而此时遍历到j+1时,只需要用前面的保留值再加上A[j+1]即得到了从i到j+1的部分子数组和。

复杂度:时间复杂度O(n^2),空间复杂度O(1)

代码:

[cpp] view plain copy
  1. int MaxSubArray(int A[], int n)  
  2. {  
  3.     int maxSum = A[0];  
  4.     int currSum;  
  5.     for(int i = 0; i < n; i++)  
  6.     {  
  7.         currSum = 0;  
  8.         for(int j = i; j < n; j++)  
  9.         {  
  10.             currSum += A[j];  
  11.             if(currSum > maxSum)  
  12.             {  
  13.                 maxSum = currSum;  
  14.             }  
  15.         }  
  16.     }  
  17.     return maxSum;  
  18. }  

三. 分治法 
分析:讲数组从中间分开,则最大子数组要么完全在组半边数组,要么在有半边数组,要么跨立在中间的分界点上,如果完全在左右半边数组,用递归解决,如果跨立在分界点上,则一定包含左半边数组的最大后缀和右半边数组的最大前缀,因此可以从分界处向前后扫。

复杂度:时间复杂度O(nlogn),空间复杂度O(1)

代码:

[cpp] view plain copy
  1. int MaxSubArray(int A[], int from, int n)  
  2. {  
  3.     if(from == to) return A[from];  
  4.     int mid = (from + to) >> 1;  
  5.     int m1 = MaxSubArray(A, from, mid);  
  6.     int m2 = MaxSubArray(A, mid + 1, to);  
  7.     int left = A[mid];  
  8.     int now = A[mid];  
  9.     for(int i = mid - 1; i >=from; i--)  
  10.     {  
  11.         now += A[i];  
  12.         left = max(left, now);  
  13.     }  
  14.     int right = A[mid + 1];  
  15.     now = A[mid + 1];  
  16.     for(int j = mid + 2; j <= to; j++)  
  17.     {  
  18.         now += A[j];  
  19.         right = max(right, now);  
  20.     }  
  21.     int m3 = left + right;  
  22.     int maxSum = max(m1, m2, m3);  
  23.     return maxSum;  
  24. }  

四. sum数组法 
分析: 
设sum[i] = A[0] +A[1] + …… +A[i] 
记S[i, j]为从子数组A[i],…..A[j]的和,则S[i, j] = sum[j] - sum[i-1], 
如何求出最大的S[i, j],一个很直观的想法就是再遍历j的时候,我们使得sum[i-1]保持最小,即可得到在j在当前的最小子数组和,另外每遍历一次j,我们就像当前的到的S[i, j]和保留值作比较,并更新maxSum。

复杂度:时间复杂度O(n),空间复杂度O(n)

代码:

[cpp] view plain copy
  1. int MaxSubArray(int A[], int n)  
  2. {  
  3.     int sum[n];  
  4.     sum[0] = A[0];  
  5.     for(int i = 1; i < n; i++)  
  6.         sum[i] = sum[i-1] + A[i];  
  7.     int maxSum = sum[0];  
  8.     int min = 0;  
  9.     for(int j = 1; j < n; j++)  
  10.     {  
  11.         if(sum[j-1] < min) min = sum[j-1];  
  12.         if(sum[j] - min > maxSum) maxSum = sum[j] - min;  
  13.     }  
  14.     return maxSum;  
  15. }  

五. 动态规划 
分析: 
上面的算法将时间复杂度降到了O(n),却将空间复杂度升到了O(1),那么能不能讲空间复杂度降到O(1)呢 答案是肯定的。

不妨设all[i]为子数组A[0]…..A[i]的最大和,start[i]为子数组A[0]…..A[i]且包含A[i]的的最大和,那么如何求出all[i]呢,观察可得,all[i] = max{all[i-1], start[i-1] + A[i], A[i]}

复杂度:时间复杂度O(n),空间复杂度O(1)

代码:

[cpp] view plain copy
  1. int MaxSubArray(int A[], int n)  
  2. {  
  3.     int all[n];  
  4.     int start[n];  
  5.     all[0] = start[0] = A[0];  
  6.     for(int i = 1; i < n; i++)  
  7.     {  
  8.         start[i] = max(start[i-1]+ A[i], A[i]);  
  9.         all[i] = max(start[i], all[i-1]);  
  10.     }  
  11.     return all[n-1];  
  12. }  

似乎空间复杂度没降下来,但其实以上程序中的数组是没必要的。


[cpp] view plain copy
  1. int MaxSubArray(int A[], int n)  
  2. {  
  3.     int nall;  
  4.     int nstart;  
  5.     nall = nstart = A[0];  
  6.     for(int i = 1; i < n; i++)  
  7.     {  
  8.         nstart = max(nstart + A[i], A[i]);  
  9.         nall = max(nstart, nall);  
  10.     }  
  11.     return nall;  
  12. }  
0 0