//最大子序列和问题的四种算法
//注意以下算法只是求出最大的和,并没有找出对应的子序列
public class MaxSubSum {
public static void main(String[] args) {
int[] a = { 4, -3, 5, -2, -1, 2, 6, -2 };
System.out.println(maxSubSum1(a));
System.out.println(maxSubSum2(a));
System.out.println(maxSubSum3(a));
System.out.println(maxSubSum4(a));
}
// 算法一,穷举法,运行时间 O(N^3)
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 thisSum = 0;
for (int k = i; k <= j; k++)
thisSum += a[k];
if (thisSum > maxSum)
maxSum = thisSum;
}
}
return maxSum;
}
// 算法二,穷举法的改进,与算法一没有本质区别
// 由三层循环改为两层,运行时间O(N^2)
public static int maxSubSum2(int[] a) {
int maxSum = 0;
for (int i = 0; i < a.length; i++) {
int thisSum = 0;
for (int j = i; j < a.length; j++) {
thisSum += a[j];
if (thisSum > maxSum)
maxSum = thisSum;
}
}
return maxSum;
}
// 算法三,采用递归思想和分治(divide-and-conquer)策略
// 如果把序列从中间分为两部分,那么最大子序列和可能在三处出现,要么整个出现在输入数据的左半部,要么整个出现在右半部,
// 要么跨越分界线。前两种情况可以递归求解,第三种情况的最大和可以通过求出前半部分(包括前半部分的最后一个元素)的最大
// 和以及后半部分(包含后半部分的第一个元素)的最大和而得到,此时将两个和相加。
// 运行时间O(NlogN)
public static int maxSubSum3(int[] a) {
return maxSumRec(a, 0, a.length - 1);
}
private static int maxSumRec(int[] a, int left, int right) {
// 基准情况(也叫终止条件)
if (left == right)
return a[left] > 0 ? a[left] : 0;
// 分成两个子问题
int center = (left + right) / 2;
// 递归求解
int maxLeftSum = maxSumRec(a, left, center);
int maxRightSum = maxSumRec(a, center + 1, right);
//左半部分最大值
int maxLeftBoardSum = 0;
int leftBoardSum = 0;
for (int i = center; i >= left; i--) {
leftBoardSum += a[i];
if (leftBoardSum > maxLeftBoardSum)
maxLeftBoardSum = leftBoardSum;
}
//右半部分最大值
int maxRightBoardSum = 0;
int RightBoardSum = 0;
for (int i = center+1; i <= right; i++) {
RightBoardSum += a[i];
if (RightBoardSum > maxRightBoardSum)
maxRightBoardSum = RightBoardSum;
}
return max3(maxLeftSum, maxRightSum, maxLeftBoardSum + maxRightBoardSum);
}
//返回三个数中最大的那个
private static int max3(int a, int b, int c){
return a >= b ? (a >= c ? a : c) : (b >= c ? b : c);
}
//算法四,最快的算法,运行时间O(N)
//核心思想:任何负的子序列不可能是最优子序列的前缀!将子序列一步步推进,遇到负的前缀就把该前缀抛弃
//该算法只对数据进行一次扫描,使得数据可以按顺序读入,在主存中不必存储数组的任何部分。在任意时刻,算法都能对
//它已经读入的数据给出子序列的正确答案(其他算法不行)。具有这种特性的算法叫联机算法(on-line algorithm)
public static int maxSubSum4(int[] a){
int maxSum = 0;
int 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;
}
}