前言
一、思路
最大字段和问题可以有三种解法:
1.穷举法
2.分治法
3.动态规划法
二、穷举法
穷举每一种可能,两到三层循环,既可以搞定。时间复杂度O(n2)或O(n3)。
private static int MaxSubSum(int[] aa) {
int T,bestI=0,bestJ=0;
int maxSum=aa[0];
for (int i = 0; i < aa.length; i++) {
for (int j = i+1; j < aa.length; j++) {
T=0;
for(k=i;k<=j;k++){
T+=aa[k];
}
if(T>maxSum) {
maxSum=T;
bestI=i;
bestJ=j;
}
}
}
System.out.println("该列从第"+(bestI+1)+"位到第"+(bestJ+1)+"位和最大,最大和为:"+maxSum);
return maxSum;
}
private static int MaxSubSum1(int[] aa) {
int T,bestI=0,bestJ=0;
int maxSum=aa[0];
for (int i = 0; i < aa.length; i++) {
T=aa[i];
for (int j = i+1; j < aa.length; j++) {
T+=aa[j];
if(T>maxSum) {
maxSum=T;
bestI=i;
bestJ=j;
}
}
}
System.out.println("该列从第"+(bestI+1)+"位到第"+(bestJ+1)+"位和最大,最大和为:"+maxSum);
return maxSum;
}
}
三、分治法
思路:
1)将给定的序列aa,分为aa[0] ~aa[n/2] 和 aa[n/2+1] ~aa[n]。
2)递归求得两端的最大子列和 MaxLeftSum和MaxRightSum。
3)从中点mid分别向两边扫描,找出跨中间跨分界线的最大子列和,MaxMidSum 。
4)MaxSum=Max(MaxLeftSum,MaxRightSum,MaxMidSum )
private static int MaxSubSum2(int[] aa,int left,int right) {
if(left==right) {
if(aa[left]>0)
return aa[left];
else
return 0;
}
int mid = (left+right)/2;
int MaxSum;
int MaxLeftSum,MaxRightSum,MaxMidSum;
int MaxLeftMidSum,MaxRightMidSum;
int LeftMidSum,RightMidSum;
MaxLeftSum = MaxSubSum2(aa,left,mid);
MaxRightSum = MaxSubSum2(aa,mid+1,right);
MaxLeftMidSum=0;MaxRightMidSum=0;
LeftMidSum=0;RightMidSum=0;
for (int i = mid; i >=left; i--) {
LeftMidSum+=aa[i];
if(LeftMidSum>MaxLeftMidSum)
MaxLeftMidSum=LeftMidSum;
}
for (int i = mid+1; i <=right; i++) {
RightMidSum+=aa[i];
if(RightMidSum>MaxRightMidSum)
MaxRightMidSum=RightMidSum;
}
MaxMidSum = MaxLeftMidSum+MaxRightMidSum;
if(MaxMidSum>MaxLeftSum)
MaxSum = MaxMidSum;
else
MaxSum = MaxLeftSum;
if(MaxMidSum>MaxRightSum)
MaxSum = MaxMidSum;
else
MaxSum = MaxRightSum;
return MaxSum;
}
由递归方程可以得出 其时间复杂度为O(nlogn)。
四、动态规划法
1)通过填写dp数组来确定最大字段和大小。
2)dp[i] 数组某一位 i 的大小,为到 i 为止的最大字段和的大小。
dp[i] = Math.max(dp[i-1]+aa[i], aa[i]);
3)下上面代码的意思是,如果dp[i-1]为负数,则dp[i]=aa[i],否者dp[i]=dp[i-1]+aa[i]。
4)例如
5)对于dp[i]来说,dp[i-1]位不为负数 表示i前面存在大于等于0的的字段,所以应该加上aa[i]去构成dp[i].
private static int MaxSubSum3(int[] aa) {
int[] dp = new int[aa.length];
int MaxSum;
dp[0] = aa[0];//初始化
MaxSum = dp[0];
for (int i = 1; i < bb.length; i++) {
dp[i] = Math.max(dp[i-1]+aa[i], aa[i]);
if(dp[i]>MaxSum) {
MaxSum = dp[i];
}
}
return MaxSum;
}
一层for循环,故其时间复杂度为O(n)。
1.输出最大字段和
记录下最后一次dp赋值给MaxSum的 i 值,拿着MaxSum依次往前去减aa数组,直到MaxSum为零为止。
private static int MaxSubSum3(int[] aa) {
int[] bdp = new int[aa.length];
int MaxSum,f=0;
dp[0] = aa[0];
MaxSum = dp[0];
for (int i = 1; i < bb.length; i++) {
dp[i] = Math.max(dp[i-1]+aa[i], aa[i]);
if(dp[i]>MaxSum) {
f=i;
MaxSum = dp[i];
}
}
int maxsum=MaxSum;
int s=0;
for(int j=f;j>=0;j--) {//减到零为 起始位置
maxsum=maxsum-aa[j];
if(maxsum==0) {
s=j;
break;
}
}
System.out.println("最大字段和为:");
for(int i=s;i<=f;i++)//输出最大字段和
System.out.print(aa[i]+" ");
System.out.println();
return MaxSum;
}