问题描述
一个有n个元素的数组,这n个元素可以是正数也可以是负数,数组中连续的一个或多个元素可以组成一个连续的子数组,一个数组可能有多个这样的连续子数组,求子数组和的最大值。
示例:
对于数组{1,-2,4,8,-4,7,-1,-5}而言,其最大和的子数组为{4,8,-4,7},最大值为15。
方法一:
暴力法。找出所有子数组,然后求出子数组之和,在所有子数组和中取最大值。
时间复杂度:O(n^3)
方法一代码:
package com.haobi;
/*
* 如何求最大子数组之和?
*/
public class Test8 {
public static void main(String[] args) {
int[] arr = {1,-2,4,8,-4,7,-1,-5};
System.out.println(maxSubArray1(arr));
}
public static int maxSubArray1(int[] arr) {
int n = arr.length;
int Max = Integer.MIN_VALUE;
int Sum = 0;
for(int i=0;i<n;i++) {
for(int j=i;j<n;j++) {
//为Sum赋初值
Sum = 0;
//框定区间
for(int k=i;k<j;k++) {
//求总和
Sum += arr[k];
}
//每个子数组的和与之前的和的最大值比较
if(Sum > Max) {
Max = Sum;
}
}
}
return Max;
}
}
方法二:
重复利用已经计算出来的子数组和。例如Sum[i,j]=Sum[i,j-1]+arr[j]。
时间复杂度:O(n^2)
方法二代码:
package com.haobi;
/*
* 如何求最大子数组之和?
*/
public class Test9 {
public static void main(String[] args) {
int[] arr = {1,-2,4,8,-4,7,-1,-5};
System.out.println(maxSubArray2(arr));
}
public static int maxSubArray2(int[] arr) {
int n = arr.length;
int Max = Integer.MIN_VALUE;
for(int i=0;i<n;i++) {
//为Sum赋初值
int Sum = 0;
for(int j=i;j<n;j++) {
Sum+= arr[j];
if(Sum > Max) {
Max = Sum;
}
}
}
return Max;
}
}
方法三:
通过动态。判断数组的最后一个元素arr[n-1]与最大子数组的关系分为以下3种情况:
(1)最大子数组包含arr[n-1],即以arr[n-1]结尾。
(2)arr[n-1]单独构成最大子数组。
(3)最大子数组不包含arr[n-1],那么问题转换为求arr[0,…,n-2]的最大子数组。
因此,假设All[i-1]表示从arr[0]到arr[i-1]中最大的一段数组之和,End[i-1]表示从arr[0]到arr[i-1]中包含arr[i-1]的最大的一算数组之和,可得到动态方程:All[i-1] = max{arr[i-1],End[i-1],All[i-2]}
时间复杂度:O(n)
方法三代码:
package com.haobi;
/*
* 如何求最大子数组之和?
*/
public class Test10 {
public static void main(String[] args) {
int[] arr = {1,-2,4,8,-4,7,-1,-5};
System.out.println(maxSubArray3(arr));
}
public static int maxSubArray3(int[] arr) {
int n = arr.length;
int[] End = new int[n];
int[] All = new int[n];
End[n-1] = arr[n-1];
All[n-1] = arr[n-1];
//为防止数组越界,提前给数组中第一个元素赋值
End[0] = All[0] = arr[0];
for(int i=1;i<n;i++) {
//求arr[i]、以arr[i]结尾的子数组 两者中的最大值
End[i] = max(End[i-1]+arr[i], arr[i]);
//状态方程
All[i] = max(End[i],All[i-1]);
}
return All[n-1];
}
public static int max(int m, int n) {
return m>n ? m:n;
}
}
方法四:
优化动态规划。可以定义两个变量保存End[i-1]与All[i-1]的值,并且可以反复利用,这样可以降低空间复杂度。
时间复杂度:O(n)
方法四代码:
package com.haobi;
/*
* 如何求最大子数组之和?
*/
public class Test11 {
public static void main(String[] args) {
int[] arr = {1,-2,4,8,-4,7,-1,-5};
System.out.println(maxSubArray3(arr));
}
public static int maxSubArray3(int[] arr) {
int n = arr.length;
//有n个数字数组的最大子数组和
int All = arr[0];
//有n个数字数组包含最后一个元素的子数组的最大和
int End = arr[0];
for(int i=1;i<n;i++) {
//求arr[i]、以arr[i]结尾的子数组 两者中的最大值
End = max(End+arr[i], arr[i]);
//状态方程
All = max(End, All);
}
return All;
}
public static int max(int m, int n) {
return m>n ? m:n;
}
}