最大子序列和的问题
给出一个序列A1,A2,…,AN,求其最大子序列和。
首先要明白两个概念:
1.序列:序列是被排成一列的对象,每个元素不是在其他元素之前,就是在其他元素之后。这里,元素之间的顺序非常重要。
2.子序列:某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置而形成的新序列。
例如对于序列:-1,3,5,6,-4,其最大子序列和是14。
下面给出这个问题的四种算法,时间复杂度各不相同。
算法1:
public static int maxSubSum1(int[] a)
{
int maxSum = 0;
for(int i = 0;i < a.length; i++)
for(int j = i;j < a.length;j++)
{
int currentSum = 0;
for(int k = i;k <= j;k++)
currentSum += a[k];
if (currentSum > maxSum)
maxSum = currentSum;
}
return maxSum;
}
分析:此奇葩算法有三个循环,时间复杂度为O(N3),循环中有太多的重复计算。比如给定序列{0,1,2,3},当i = 0时,j = 0~3,此时currentSum分别计算为0,0+1,0+1+2,0+1+2+3,计算0+1+2时不能利用前面已经算好的0+1,属于重复计算。
算法2:
public static int maxSubSum2(int[] a)
{
int maxSum = 0;
for(int i = 0;i < a.length; i++)
{
int currentSum = 0;
for(int j = i;j < a.length;j++)
{
currentSum += a[j];
if (currentSum > maxSum)
maxSum = currentSum;
}
}
return maxSum;
}
分析:算法2是对算法1的改进算法,去除了一个for循环,时间复杂度为O(N2),主要提升在于第二个for循环,计算时利用了前面的结果,不像算法1还要一个for循环重新开始计算那么奇葩。
算法3:
public static int maxSubSum3(int[] a,int left,int right)
{
if(left == right)
if(left > 0)
return a[left];
else
return 0;
int center = (left + right) / 2;
int maxLeftSum = maxSubSum3(a,left,center);
int maxRightSum = maxSubSum3(a,center + 1,right);
int maxLeftCurrentSum = 0;
int leftCurrentSum = 0;
for(int i = center;i >= left;i--)
{
leftCurrentSum += a[i];
if(leftCurrentSum > maxLeftCurrentSum)
maxLeftCurrentSum = leftCurrentSum;
}
int maxRightCurrentSum = 0;
int rightCurrentSum = 0;
for(int i = center + 1;i <= right;i++)
{
rightCurrentSum += a[i];
if(rightCurrentSum > maxRightCurrentSum)
maxRightCurrentSum = rightCurrentSum;
}
return max(maxLeftSum,maxRightSum,maxLeftCurrentSum + maxRightCurrentSum);
}
public static int max(int a,int b,int c)
{
int[] arr = new int[]{a,b,c};
int max = 0;
for(int i = 0;i < arr.length;i++)
max = max >= arr[i]?max:arr[i];
return max;
}
分析:该方法采用一种“分治”策略,把序列分成两个大致相等的子序列,分别递归求解,再合并到一起。最大子序列可能出现在三处,左半部分,右半部分,横跨左右两部分。其中左半部分和右半部分可以用递归求解,第三种情况可以通过分别求出左半部分和右半部分的最大和,再相加而得到。时间复杂度为O(N ㏒N)。
算法4:
public static int maxSubSum4(int[] a)
{
int maxSum = 0;
int currentSum = 0;
for(int i = 0;i < a.length;i++)
{
currentSum += a[i];
if(currentSum > maxSum)
maxSum = currentSum;
else if(currentSum < 0)
currentSum = 0;
}
return maxSum;
}
分析:此算法的时间复杂度为O(N),只有一次循环,循环中遇到负值会把currentSum设为0重新开始。