最大子序列和
给定(可能有负的)整数A1,A2,…,AN,求子序列和的最大值。
例如:对于输入-2,11,-4,13,-5,-2,答案为20(从A2到A4)
法1:穷举所有的可能
时间复杂度为
O(n^{3})
public static int maxSubSum1(int a[]){
int max=0;
for (int i=0;i<a.length;i++){
for (int j=i;j<a.length;j++){
int s=0;
for (int k=i;k<=j;k++){
s+=a[k];
}
if (s>max){
max=s;
}
}
}
return max;
}
法2:撤销一个for循环来避免三次的运行时间
时间复杂度为
O(n^{2})
public static int maxSubSum2(int a[]){
int max=0;
for (int i=0;i<a.length;i++){
int s=0;
for (int j=i;j<a.length;j++){
s+=a[j];
if (s>max){
max=s;
}
}
}
return max;
}
法3:使用分治法,先分别计算出左半部分、右半部分的最大子序列和,然后计算出跨越中间部分的最大子序列和,求三个中的最大值即可。
时间复杂度为
O(nlogn)
//分治法
public static int maxSubSum3(int a[],int left,int right){
int max=0;
if (left==right){
if (a[left]>0)
return a[left];
else
return 0;
}
int mid=(right+left)/2;
int l=maxSubSum3(a,left,mid);//求左半部分的最大子序列和
int r=maxSubSum3(a,mid+1,right);//求右半部分的最大子序列和
/*求跨越中间元素的最大子序列和,
左边的从中间元素向左计算,右边的从中间元素的下一个元素向右计算
*/
int maxLeftBorderSum=0;
int leftBorderSum=0;
for (int i=mid;i>=left;i--){
leftBorderSum += a[i];
if (leftBorderSum>maxLeftBorderSum)
maxLeftBorderSum=leftBorderSum;
}
int maxRightBorderSum=0;
int rightBorderSum=0;
for (int i=mid+1;i<=right;i++){
rightBorderSum += a[i];
if (rightBorderSum>maxRightBorderSum)
maxRightBorderSum=rightBorderSum;
}
int max_lr=Math.max(l,r);
max=Math.max(max_lr,maxLeftBorderSum+maxRightBorderSum);
return max;
法4:动态规划
令状态dp[i]表示以A[i]作为末尾的连续序列的最大值
当dp[i-1]>0,dp[i]=dp[i-1]+A[i]
否则,dp[i]=A[i]
所以,dp[i]=max(dp[i-1]+a[i],a[i]);
O(n)
//动态规划
public static int maxSubSum4(int a[]){
int maxSum=0,thisSum=0;
for (int i=0;i<a.length;i++){
thisSum+=a[i];
if (thisSum>maxSum)
maxSum=thisSum;
else if (thisSum<0)
thisSum=0;
}
return maxSum;
}