一个数组的最大子数组和可能存在于三种情况中:
1. 该数组的左子数组中
2.该数组的右子数组中
3.可能横跨左右子数组
所以,我们可以将问题分解成一个个的子问题,其中情况1和2可用递归求解,而情况3则需要计算
s1=max{} (1 <= i <= n/2), s2=max{
} (n/2+1 <= j <=n),其中情况3中s1必然包含了
,
s2中必然包含了,因为情况3跨越了左右子数组。s1 + s2为情况3的最大子数组。
代码(记录了最大数组和的范围):
/*
* 利用分治法求最大子数组问题
*/
public class TestOne {
public static int leftFlag; //负责记录最大子数组和的开始下标
public static int rightFlag; //负责记录最大子数组和的结束下标
//求最大子数组跨越了两个数组的情况
public static int midArray(int[] a,int low,int mid,int high) {
// 求low...mid之间的最大数组和
int leftSum = -10000;
int sum = 0;
for(int i = mid;i>=low;i--)
{
sum += a[i];
if(sum > leftSum)
{
leftSum = sum;
leftFlag = i;
}
}
//求mid+1...high之间的最大数组和
int rightSum = -10000;
sum = 0;
for(int i = mid+1;i<=high;i++)
{
sum += a[i];
if(sum > rightSum)
{
rightSum = sum;
rightFlag = i;
}
}
return leftSum + rightSum;
}
public static int subArray(int[] a,int low,int high) {
if(high==low)
return a[low];
int mid = (low + high) / 2;
int leftSum = subArray(a,low,mid); //求出左边数组的最大子数组和
int rightSum = subArray(a,mid+1,high); //求出右边数组的最大子数组和
int midSum = midArray(a,low,mid,high); //求出跨越左右两个数组的最大子数组和
if(leftSum > rightSum && leftSum > midSum)
return leftSum;
else if(rightSum > leftSum && rightSum > midSum)
return rightSum;
else
return midSum;
}
public static void main(String[] args) {
int[] a = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
System.out.println(subArray(a,0,a.length-1));
System.out.println(leftFlag + " " + rightFlag);
}
}
代码(只计算了最大数组和,个人觉得这个更简单,更容易理解):
public class Main {
//分治法 最大连续和
public static void main(String[] args) {
int[] arr = new int[] {-1,2,1};
int res = fun(arr,0,arr.length);
System.out.println(res);
}
/**
* 求arr中[low,high)的最大连续和
* @param arr
* @param low
* @param high
* @return
*/
public static int fun(int[] arr,int low,int high) {
if(high-low==1) //如果只有一个元素
return arr[low];
int mid = low + (high-low)/2;
int maxs = max(fun(arr,low,mid),fun(arr,mid,high)); //求左区间最大连续和、右区间最大连续和,并得到两者最大值
//求出包含arr[mid]在内的左区间的最大连续和
int sum = 0;
int res1 = arr[mid-1];
for(int i = mid-1; i >= low; i--)
res1 = max(res1,sum+=arr[i]);
//求出包含arr[mid]+1在内的右区间的最大连续和
sum = 0;
int res2 = arr[mid];
for(int i = mid; i < high; i++)
res2 = max(res2,sum+=arr[i]);
return max(maxs,res1+res2);
}
public static int max(int x, int y) {
return (x>y) ? x : y;
}
}
版权声明:本文为hjl_heart原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。