Question:
输入一组整数,求出这组数字子序列和中最大值。也就是只要求出最大子序列的和,不必求出最大的那个序列。例如:
序列:-2 ,11, -4, 13, -5, -2,则最大子序列和为20。
序列:-6 ,2, 4, -7, 5, 3, 2 ,-1 ,6, -9 ,10 ,-2,则最大子序列和为16。
算法1:穷举法
/**
* 穷举法
* 时间复杂度0(n^3)
* @param arr
* @return
*/
public static int maxSubseqSum1(int[] arr){
int thisSum,maxSum = 0;
int i,j,k;
for (i = 0; i < arr.length; i++) {//i是子列左端位置
for (j = i; j < arr.length; j++) {//j是子列右端位置
thisSum = 0;//thisSum是从arr[i]到a[j]的子列和
for (k = i; k < j; k++) {
thisSum += arr[k];
}
if (thisSum > maxSum) {//如果刚得到这个子列和更大
maxSum = thisSum;//则更新结果
}//j循环结束
}//i循环结束
}
return maxSum;
}
算法2:改进的穷举法,去掉一层for循环,去掉重复操作
/**
* 穷举法之二。也是一种穷举法,相比较第一种穷举法撤出了一层for循环,更加直观。而且没有多余重复的操作
* 时间复杂度0(n^2)
* @param arr
* @return
*/
public static int maxSubseqSum2(int[] arr){
int thisSum,maxSum = 0;
int i,j;
for (i = 0; i < arr.length; i++) {//i是子列左端位置
thisSum = 0; //thisSum是从arr[i]到arr[j]的子列和
for (j = i; j < arr.length; j++) {//j是子列右端位置
thisSum += arr[j];
/*对于相通的i,不同的j,只要在j-1次循环的基础上累加1项即可*/
if (thisSum > maxSum) {//如果刚得到这个子列和更大
maxSum = thisSum;//则更新结果
}
}//j循环结束
}//i循环结束
return maxSum;
}
算法3:递归法(分治法)
声明:这里借鉴别人的博文:分治法借鉴博客
/**
* 分而治之
* 时间复杂度0(nlogn)
* @param arr
* @return
*/
public static int maxSubseqSum3(int[] arr,int left,int right){
if (left == right) {
if (arr[left] > 0) {
return arr[left];
}else{
return 0;
}
}
int center = (left+right)/2;
int maxleftSum = maxSubseqSum3(arr, left, center);
int maxRightSum = maxSubseqSum3(arr, center+1, right);
//求出以左边对后一个数字结尾序列的最大值
int maxleftBorderSum = 0,leftBorderSum = 0;
for (int i = center; i >= left; i--) {
leftBorderSum += arr[i];
if (leftBorderSum > maxleftBorderSum) {
maxleftBorderSum = leftBorderSum;
}
}
//求以右边对后一个数字结尾的序列最大值
int maxRightBorderSum = 0,rightBorderSum = 0;
for (int j = center+1; j <= right; j++) {
rightBorderSum += arr[j];
if (rightBorderSum > maxRightBorderSum) {
maxRightBorderSum = rightBorderSum;
}
}
//返回左边最大,右边最大,和跨域最大的一个数字
int sum = maxleftBorderSum+ maxRightBorderSum;
if(maxleftSum >= maxRightSum && maxleftSum >= sum)
return maxleftSum;
else if(maxRightSum >= maxleftSum && maxRightSum >= sum)
return maxRightSum;
else
return sum;
}
算法4:在线处理(动态规划)
将子序列与其子子序列进行问题分割, 得到状态转移方程:MaxSum[i] = max{MaxSum[i-1]+A[i],MaxSum[i-1]+A[i]}
其中MaxSum[i]表示数组前i-1个元素的和
/**
* 在线处理
* 时间复杂度0(n) 线性算法
* @param arr
* @return
*/
public static int maxSubseqSum4(int[] arr){
int thisSum = 0,maxSum = 0;
int i,j;
for (i = 0; i < arr.length; i++) {//i是子列左端位置
thisSum += arr[i]; //向右累加
if(thisSum > maxSum)
maxSum = thisSum;//发现更大和则更新当前结果
else if(thisSum < 0)//如果当前子列和为负数
thisSum = 0;//则不可能使后面的部分和增大,抛弃之
}//i循环结束
return maxSum;
}
简单测试用例:由于时间较短,所以采用循环运行N次的办法累加时间
public static void main(String[] args) {
int[] arr1 = new int[] { -2, 11, -4, 13, -5, -2 };
int[] arr2 = new int[] { -6, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4,
-7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10,
-2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1,
6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5,
3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2,
4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9,
10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2,
-1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7,
5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2,
2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6,
-9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2, 2, 4, -7, 5, 3, 2, -1, 6, -9, 10, -2 };
int N = 1000;// 测试算法执行次数
System.out.println("maxSubseqSum1:" + maxSubseqSum1(arr2));
System.out.println("maxSubseqSum2:" + maxSubseqSum2(arr2));
System.out.println("maxSubseqSum3:" + maxSubseqSum3(arr2, 0, arr2.length - 1));
System.out.println("maxSubseqSum4:" + maxSubseqSum4(arr2));
long timeStart1 = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
maxSubseqSum1(arr2);
}
long timeEnd1 = System.currentTimeMillis();
System.out.println("算法1执行" + N + "次的时间,单位毫秒:" + (timeEnd1 - timeStart1));
long timeStart2 = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
maxSubseqSum2(arr2);
}
long timeEnd2 = System.currentTimeMillis();
System.out.println("算法2执行" + N + "次的时间,单位毫秒:" + (timeEnd2 - timeStart2));
long timeStart3 = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
maxSubseqSum3(arr2, 0, arr2.length - 1);
}
long timeEnd3 = System.currentTimeMillis();
System.out.println("算法3执行" + N + "次的时间,单位毫秒:" + (timeEnd3 - timeStart3));
long timeStart4 = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
maxSubseqSum3(arr2, 0, arr2.length - 1);
}
long timeEnd4 = System.currentTimeMillis();
System.out.println("算法4执行" + N + "次的时间,单位毫秒:" + (timeEnd4 - timeStart4));
}
测试环境:
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
OS:windows10
Eclipse:4.5.2
测试结果: