下面介绍五种方法求解最大子数组的和:其中包含蛮力算法,以及其优化;动态规划算法,以及其优化算法
package datastruct.usearray;
import java.util.Scanner;
/**
*
* 如何求最大子数组的和
*/
public class GetSumOfSubArray {
//方法一:蛮力算法 时间复杂度为:o(n^3)
private static void getSumOfSubArray(int array[]) {
int n=array.length;
int thisSum,maxSum=Integer.MIN_VALUE,k,i,j;
for ( i = 0; i < n; i++) {
for ( j = i; j <n; j++) {
thisSum=0;
System.out.print("子数组有:");
for ( k=i;k<=j;k++) {//k 为i,j分别为子数组的起始位置
thisSum=thisSum+array[k];
System.out.print(array[k]+" ");//可以输出所有的子数组是哪些
}
System.out.println();
if (thisSum>maxSum) {
maxSum=thisSum;
}
}
}
System.out.println("方法一:最大子数组之和为:"+maxSum);
}
//方法二:重复利用已经计算的子数组和,相比较方法一,时间复杂度为:o(n^2)
private static void method2 (int array[]) {
int n=array.length;
int maxSum=Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
int sum=0;
for (int j = i; j < n; j++) {
sum=sum+array[j];//相比较方法一:利用了已经计算的子数组和
if (sum>maxSum) {
maxSum=sum;
}
}
}
System.out.println("方法二:最大子数组之和为:"+maxSum);
}
/**
* 利用动态规划解题:该方法时间复杂度为:o(n),但是额外使用了两个数组空间,其空间复杂度为:o(n)
* 解题思路:对于一个数组,求最大子数组之和,我们可以分为三部分:
* 我们以最后一个元素array[n-1]为例子:
* 1:array[n-1]自己构成最大的子数组
* 2:包含array[n-1]的最大子数组,即以array[n-1]结尾,我们用End[n-1]表示
* 3:不包含array[n-1]的最大子数组,那么求array[0]...array[n-1]的子数组,可以转化为求
* array[0]...array[n-2]的最大子数组
*
* 由以上可知:All[n-1]=max{array[n-1],End[n-1],All[n-1]}
* All[n-1]表示为:array[0]...array[n-1]的最大子数组之和
*/
private static int max(int a,int b){
return a>b?a:b;
}
private static void method3(int array[]) {
int n=array.length;
int End[]=new int [n];
int All[]=new int [n];
//初始化:当数组中只有一个元素时
End[0]=All[0]=array[0];
End[n-1]=All[n-1]=array[n-1];
for (int i = 1; i <n; i++) {
End[i]=max(End[i-1]+array[i], array[i]);
All[i]=max(End[i], All[i-1]);
}
System.out.println("方法三:最大子数组之和为:"+All[n-1]);
}
/**
* 优化动态规划
* 为了进一步降低空间复杂度,我们可以定义两个变量用来保存方法三中的
* End[i-1]和All[i-1]
*
*/
private static void method4(int array[]) {
int n=array.length;
int nEnd=array[0]; //n个元素的最大组数组之和
int nAll=array[0];//包含组后一个元素的子数组之和
for (int i = 1; i <n ; i++) {
nEnd=max(nEnd+array[i], array[i]);
nAll=max(nEnd, nAll);
}
System.out.println("方法四:最大子数组之和为:"+nAll);
}
/**
* 方法五:求出最大数组之和,并得到最大子数组
*/
private static int begin=0;//记录最大子数组的起始位置
private static int end=0; //记录最小子数组的结束位置
private static void method5(int array[]) {
int maxSum=Integer.MIN_VALUE; //子数组最大值
int nSum=0;//包含子数组最后一位的最大值
int nstart=0;
for (int i = 0; i < array.length; i++) {
if (nSum<0) {
nSum=array[i];
nstart=array[i];
}else {
nSum+=array[i];
}
if (nSum>maxSum) {
maxSum=nSum;
begin=nstart;
end=i;
}
}
System.out.println("方法五:最大子数组之和为:"+maxSum);
System.out.println("起始位置为:"+begin+" 结束位置为:"+end);
System.out.println("最大数组是:");
for (int i = begin; i <=end; i++) {
System.out.print(array[i]+" ");
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("输入数组元素个数:");
int n=sc.nextInt();
System.out.println("输入"+n+"个数组元素:");
int [] arry=new int [n];
for (int i = 0; i <n; i++) {
arry[i]=sc.nextInt();
}
// 调用方法一:
getSumOfSubArray(arry);
// 调用方法二:
method2(arry);
// 调用方法三:
method3(arry);
// 调用方法四:
method4(arry);
// 调用方法五:
method5(arry);
}
}
结果如下: