最大子序列和
給定(可能有負的)整數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;
}