一 问题描述:
输入一组整数,求出这组数字子序列和中最大值。也就是只要求出最大子序列的和,不必求出最大的那个序列。例如:
序列:-3 18 -4 5 -5 -2,则最大子序列和为23。
二 解法1:穷举解法
1 int maxsequence(int a[],int len){ 2 int maxsum = 0; 3 for(int i = 0;i < len;i++){ 4 for(int j = i;j<len;j++){ 5 int newsum = 0; 6 for(int k = i;k <=j;k++){ 7 newsum+=a[k]; 8 } 9 if(newsum > maxsum) 10 maxsum = newsum; 11 } 12 } 13 return maxsum; 14 }
这个算法本身是非常简单和容易理解的,通过三个for循环直接给出所有可能出现的子序列,从中选出一个最大值即可。但是该方法时间复杂度真的是太大了,达到了O(n3),有很多操作完成是重复性的操作。例如当i = 0时,j = 1 时:该算法会计算a[0]+a[1],而当i = 0,j = 2时候,该算法会计算a[0]+a[1]+a[2],显然a[0]+a[1] 被重复计算了。
三 解法2:穷举解法2
1 int maxsequence2(int a[],int len){ 2 int maxsum = 0; 3 for(int i = 0;i < len;i++){ 4 int newsum = 0; 5 for(int j = i;j<len;j++){ 6 newsum+=a[j]; 7 if(newsum > maxsum) 8 maxsum = newsum; 9 } 10 } 11 return maxsum; 12 }
这个算法本身其实也是非常容易理解的,它不过是上面那个解法的改进,当我们计算i..j的值时候,i--j-1已经被计算过了,可以直接利用,不用重新计算一次i..j-1。其时间复杂度为O(n2),当然了,很明显的,这个时间复杂度也还是非常高的,在计算机里面,一个算法复杂度达到O(n2)其实还是一件比较可怕的事情。
四 解法3:分治法
int max3(int i, int j, int k) { if (i>=j && i>=k) return i; else if(j >= i && j >= k) return j; else return k; } int max_seq(int a[],int left,int right) { if(left > right) return 0; if(left == right) return a[left]; int mid = (left + right)/2; int lmax=a[mid], lsum = 0; for(int i = mid;i >=left;i--){ lsum += a[i]; if (lsum > lmax) lmax = lsum; } int rmax = a[mid+1]; int rsum = 0;
for(int i = mid+1;i<=right;i++){ rsum += a[i]; if(rsum > rmax) rmax = rsum; } return max3(lmax+rmax,max_seq(a,left,mid),max_seq(a,mid+1,right)); } int maxsequence3(int a[],int len){ return max_seq(a,0,len-1); }
这个算法的核心就是分治思想。我们假设将我的已经知道的序列分成左右两部分,那么最大子序列存在的位置显然有三种可能:
(1) 存在在序列的左部
(2) 存在在序列的右部
(3) 既存在在序列的左部又存在在序列的右部。
显然如果是情况(3),是比较容易算出来的。如果是情况(1)和 (2)的话,我们可以将左部和右部当中一个重新的序列计算,那么新的序列又可以分成左右部分,重复以上即可。该算法的时间复杂度了O(n * lg(n))。
五 解法四:动态规划
int maxsequence4(int a[],int len){ int maxsum = a[0]; int maxnew = a[0]; for(int i = 1;i < len;i++){ if(maxnew <= 0) maxnew = a[i]; else maxnew += a[i]; if(maxnew > maxsum) maxsum = maxnew; } return maxsum; }
该算法的时间复杂度显然就是O(n),对于计算机来说当然是非常友好的一件事情了,当是对于人来说就不是那么友好了(当然了,神人除外)。我们可以想一下,最大的子序列有什么特点呢?那就是其前m个元素之和也一定不能小于0,为什么?我们假设序列a的长度为len,假设a[i].....a[j],表示为a的最大子序列。如果sum[i->i+m]< 0,那么sum[i+m+1->j] > sum[i->j],矛盾。
代码体现:
if(maxnew <= 0)
maxnew = a[i];