问题描述:一个有n个元素的数组,这n个元素可以是正数也可以是负数,数组中连续的一个或多个元素可以组建成一个连续的子数组,一个数组可以有多个这样的连续子数组,求子数组和的最大值,并找出最大的子数组,例如,对于数组{1,-2,4,8,-4,7,-1,-5},其最大和的子数组是{4,8,-4,7},最大值为15。
方法一:用比较蛮力的方法并重复利用已经计算过的子数组和
/**
* 求连续子数组的最大和
* @param array
* @return
*/
public static int maxArray(int[] array) {
int length = array.length;
int max = Integer.MIN_VALUE;
int i = 0,j = 0; //定义两个数组 下标,i代表子数组前面元素下标,j代表后面的
int tim = 0,pwd = 0; //定义这两个变量是为了保存最大和子数组的第一个元素和最后一个元素的下标位置
for(i = 0;i < length;i++) {
int sum = 0;
for(j = i;j < length;j++) {
sum += array[j];
if(sum > max) {
tim = i;
pwd = j;
max = sum;
}
}
}
System.out.println("最大子数组: ");
while(tim <= pwd) {
System.out.print(array[tim]+" ");
tim++;
}
System.out.println();
return max;
}
方法二:利用动态规划方法实现
思路:
首先可以根据数组的最后一个元素array[n-1]与最大子数组的关系分为以下三种情况:
- 最大和子数组包含array[n-1],即以array[n-1]结尾;
- array[n-1]单独构成最大子数组;
- 最大子数组不包含array[n-1],那么求array[0,……,n-1]的最大子数组可以转换为求array[0,……,n-2]的最大子数组(又回到了第一步,只不过此时原数组变成了array[0,……,n-2])。
通过上述分析:我们可以假设已经计算出(array[0],……,array[i-1])的最大和子数组的和为All[i-1],同时也计算出(array[0],……,array[i-1])包含array[i-1]的最大的一段子数组和为End[i-1],则可以得出如下关系:All[i-1] = max{array[i-1],End[i-1],All[i-2]};为了节省空间,我们定义两个变量maxEnd、maxAll来保存End[i-1]与All[i-1]的值,并且反复利用。
/**
* 求最大值
* @param m
* @param n
* @return
*/
public static int max(int m,int n) {
return m > n ? m : n;
}
/**
* 动态规划求最长公共子序列
* @param array
* @return
*/
public static int new_maxArray(int[] array) {
int length = array.length;
int maxAll = array[0];
int maxEnd = array[0];
int start = 0; //记录最大和子数组的起始下标
int end = 0; //记录最大和子数组的结束下标
for(int i = 0;i < length;i++) {
maxEnd = max(maxEnd+array[i],array[i]);
if(maxEnd == array[i]) { //如果maxEnd == array[i],那就意味着最大和子数组的起始位置换了
start = i;
}
maxAll = max(maxEnd,maxAll);
if(maxAll == maxEnd) { //★后面有图解析
end = i;
}
}
System.out.println("最大和子数组");
while(start <= end) {
System.out.print(array[start]+" ");
start++;
}
System.out.println();
return maxAll;
}
★: