最大子段和问题描述
给定由 n 个整数(可能为负整数)组成的序列a1,a2,a3...an,求该数列中连续子段和最大!
例如:当(a1,a2,a3,a4,a5)=(-2,11,-4,13,-5,-2)时,最大字段和为 20 (11 + (-4) + 13);
以下例子都是以int data[6] = {-2,11,-4,13,-5,-2}; int n = 6;
初始化数组:
//初始化数组
private static Integer[] array = {-2, 11, -4, 13, -5, -2};
算法一:对所有满足0<=i<=j<=n的(i,j)整数对进行迭代,对每个整数对,程序都要计算array[i...j]的总和,并检验该总和是否大于迄今为止的最大总和
这段代码简洁明了,便于理解,但是程序执行的速度很慢,时间复杂度为O(n^3)
/**
* 时间复杂度为 O(n^3)
*/
public static Integer maxSum1() {
int maxSum = 0; //存储最大子段和
int tempSum; //临时存储最大子段和
for (int i = 0; i < array.length - 1; i++) {
for (int j = i; j < array.length; j++) {
tempSum = 0;
for (int k = i; k <= j; k++) {
tempSum += array[k];
if (tempSum > maxSum) {
maxSum = tempSum;
}
}
}
}
return maxSum;
}
算法二:对于算法一有一个明显的缺点,大量的计算重复。大家可以注意到:
这段代码简洁明了,便于理解,相比算法一程序执行的速度较快,时间复杂度为O(n^2)
注意:array[i...j]的总和与前面计算出的总和(array[i...j-1])密切相关,只需要在其基础上累加即可,无需大量重复计算!
/**
* 时间复杂度为 O(n^2)
*/
public static Integer maxSum2() {
int maxSum = 0; //存储最大子段和
int tempSum; //临时存储最大子段和
for (int i = 0; i < array.length - 1; i++) {
tempSum = 0;
for (int j = i; j < array.length; j++) {
tempSum += array[j];
if (tempSum > maxSum) {
maxSum = tempSum;
}
}
}
return maxSum;
}
算法三:可以采用分治算法求解,采用二分法进行二分,然后进行递归求解,分别求出左边连续子段和最大值,右边连续子段和最大值,以及左边和右边连续子段和最大值之和,三者进行比较,从中选择一个最大值进行返回!(这个值即就是当前划分的小区间中最大值)
注意:这段代码不太便于理解,但是程序相对于算法二执行的速度快,时间复杂度为O(n*logn)
/**
* 采用分治算法
* 时间复杂度为 O(n*logN)
*/
public static Integer maxSum3(int left, int right) {
int maxSum = 0;
if (left == right) { //递归结束条件
if (array[left] > 0) {
maxSum = array[left];
} else {
maxSum = 0;
}
return maxSum;
}
int mid = (left + right) / 2;
int leftMaxSum = maxSum3(left, mid); //递归求解左部分最大值
int rightMaxSum = maxSum3(mid + 1, right); //递归求解右部分最大值
//求解左边最大值和右边最大值之和
int leftMax = 0; //记录左边最大值
int leftMaxTemp = 0; //记录左边最大值的临时变量
for (int i = mid; i >= left; i--) {
leftMaxTemp += array[i];
if (leftMaxTemp > leftMax) {
leftMax = leftMaxTemp; //左边最大值放在 leftMax
}
}
int rightMax = 0;
int rightMaxTemp = 0;
for (int j = mid + 1; j <= right; j++) {
rightMaxTemp += array[j];
if (rightMaxTemp > rightMax) {
rightMax = rightMaxTemp; //右边最大值放在 rightMax
}
}
maxSum = leftMax + rightMax;//(左边最大值和右边最大值之和)计算第 3 种情况的最大子段和
//比较(左边最大值)和(右边最大值)以及(两边最大值之和)进行比较,从中选择一个最大值返回
if (maxSum < leftMaxSum) {
maxSum = leftMaxSum;
}
if (maxSum < rightMaxSum) {
maxSum = rightMaxSum;
}
return maxSum;
}
算法四:使用动态规划来求解 ,由data[]数组我们易知,当maxSumTemp > 0时,maxSumTemp = data[i] + maxSumTemp (越加越大),否则maxSumTemp = data[i](不然越加越小)
这段代码便于理解,但是程序相对于算法三执行的速度最快,时间复杂度为O(n)
/**
* 时间复杂度为 O(n)
*/
public static Integer maxSum4() {
int maxSum = array[0];
int maxSumTemp = array[0]; //初始化
for (int i = 1; i < array.length; i++) {
if (maxSumTemp > 0) { //最大值临时变量只有大于零,才可能越加越大
maxSumTemp += array[i];
} else { //最大值临时变量只有小于零,直接等于data[i],否则越加越小
maxSumTemp = array[i];
}
if (maxSumTemp > maxSum) { //判断赋值
maxSum = maxSumTemp;
}
}
return maxSum;
}
测试代码:
public static void main(String[] args) {
// Integer maxSum = maxSum1();
// Integer maxSum = maxSum2();
// Integer maxSum = maxSum3(0, array.length - 1);
Integer maxSum = maxSum4();
System.out.println("maxSum = " + maxSum);
}
每天进步一点点,日记月累就会变成大牛!