一。
1.什么是子数组:数组中一段连续的序列
2.什么是最大子数组:子数组各个值相加的和最大
3.为什么要对实现最大子数组这一问题进行分治求解:通过计算得知,暴力枚举的时间复杂度在O(),对暴力枚举进行优化后的优化枚举的时间复杂度在O(
),我们为了找到用时更小的算法,因此想到采用分治来实现。
4.什么是分治:
分而治之三步骤:分解原问题,解决子问题,合并问题解
1.分解原问题:将原问题分解为若干个规模较小,相对独立,与原问题形式相同的子问题。
2.解决子问:若子问题规模较小且易于解决时,则直接解。否则,递归地解决各子问题。
3.合并:将各子问题的解合并为原问题的解。
5.用分治的思维来解决最大子数组问题有些类似于归并排序,在归并排序合并这一操作的时候把比较和交换移动步骤改为左半段求最大子数组和,右半段求最大子数组和,左半段和右半段结合起来求最大子数组和(也就是求跨中点的最大子数组和),而且我们会发现合并的时候都有左半段的右边和右半段的左边参与相加,考虑到这些,我们就很容易借鉴归并排序的递归思维去解决最大子数组问题。
因此该问题的重点就在于如何合并问题解,求解跨中点的最大子数组和。
6.分治算法实现最大子数组的时间复杂度为O(nlogn)
二。
S1:数组X[1...n/2]的最大子数组
S2:数组X[n/2+1...n]的最大子数组
S3:跨中点的最大子数组
求解S3具体步骤:
求解Sleft
1.记mid=n/2
2.从X[mid]向前遍历求和,并记录最大值
3.以X[mid]为结尾的最大子数组之和
求解Sright
方法同上
合并
S3=Sleft+Sright
三,实现过程
一.java中函数的用法
形式参数:在定义函数时给的参数叫做形式参数(形参),参数叫什么名字与功能无关,当然需要遵循见名思议的原则。
实际参数:在调用函数时给的参数叫做实际参数(实参),实参在调用时必须具有实际的值。
我们调用方法是通过值传递的方式把实参的值传递给方法的形参,而且Java语言中只有值传递
重载(overload):在一个类中有两个或者两个以上同名的方法,但是参数不同(两个方法的参数的个数不同,参数的类型不同),跟返回值无关
二,代码框架
class MaxSubarraySum{
//递归求解函数
public int ToMaxSubarray(int[] arr,int left,int right){
}
//求跨中点的最大子数组和
public int AddMaxSubarray(int[] arr,int left,int mid,int right){
}
}
public class mod1{
public static void main(String[] args){
MaxSubarraySum subarray=new MaxSubarraySum();
int[] arr={1,-2,4,5,-2,8,3,-2,6,3,7,-1};
int finallsum=subarray.ToMaxSubarray(arr,0,arr.length-1);//用finallsum来返回最终结果
System.out.println("最大子数组和为:"+finallsum);
}
}
三,递归求解函数的具体代码
public int ToMaxSubarray(int[] arr,int left,int right){
if(left>=right){
return arr[left];
}//如果arr数组中仅有一个值,直接输出arr[0]
int mid=(left+right)/2;
int leftmax=ToMaxSubarray(arr,0,mid);
int rightmax=ToMaxSubarray(arr,mid+1,right);
int tomidmax=AddMaxSubarray(arr,left,mid,right);
//求leftmax,rightmax,tomidmax三个数中最大的一个并返回其值
int temp=leftmax;
if(temp<=rightmax)
temp=rightmax;
if(temp<=tomidmax)
temp=tomidmax;
return temp;
}
四,求跨中点的最大子数组和的具体代码
注:
正无穷大:用一个正数除以0将得到一个正无穷大,通过Double或Float的POSITIVE_INFINITY表示。
负无穷大:用一个负数除以0将得到一个负无穷大,通过Double或Float的NEGATIVE_INFINITY表示。
非数:0.0除以0.0或对一个负数开放将得到一个非数,通过Double或Float的NaN表示。
public int AddMaxSubarray(int[] arr,int left,int mid,int right){
int sum=0;
int Sleft=(int)Double.NEGATIVE_INFINITY;
int Sright=(int)Double.NEGATIVE_INFINITY;
for(int i=mid;i>=0;i--){
sum=sum+arr[i];
if(Sleft<=sum){
Sleft=sum;
}
}
for(int j=mid+1;j<right;j++){
sum=sum+arr[j];
if(Sright<=sum){
Sright=sum;
}
}
return Sleft+Sright;
}
五,第一次调试
检查:
改:j<=right 后,结果变为62
六,第二次调试
原来是求右半边包含右半边右边元素的最大值得时候,没有给sum归零。
七,最终代码
class MaxSubarraySum{
//递归求解函数
public int ToMaxSubarray(int[] arr,int left,int right){
if(left==right){
return arr[left];
}//递归出口
int mid=(left+right)/2;
int leftmax=ToMaxSubarray(arr,left,mid);//左半段最大值
int rightmax=ToMaxSubarray(arr,mid+1,right);//右半段最大值
int tomidmax=AddMaxSubarray(arr,left,mid,right);//跨中点最大值
//求leftmax,rightmax,tomidmax三个数中最大的一个并返回其值
int temp=leftmax;
if(rightmax>=temp)
temp=rightmax;
if(tomidmax>=temp)
temp=tomidmax;
return temp;
}
//求跨中点的最大子数组和
public int AddMaxSubarray(int[] arr,int left,int mid,int right){
int sum=0;
int Sleft=(int)Double.NEGATIVE_INFINITY;
int Sright=(int)Double.NEGATIVE_INFINITY;
//左半边包含左半边右边元素的最大值
for(int i=mid;i>=left;i--){
sum=sum+arr[i];
if(sum>Sleft){
Sleft=sum;
}
}
//右半边包含右半边右边元素的最大值
sum=0;
for(int j=mid+1;j<=right;j++){
sum=sum+arr[j];
if(sum>Sright){
Sright=sum;
}
}
return Sleft+Sright;
}
}
public class mod1{
public static void main(String[] args){
MaxSubarraySum subarray=new MaxSubarraySum();
int[] arr={1,-2,4,5,-2,8,3,-2,6,3,7,-1};
int finallsum=subarray.ToMaxSubarray(arr,0,arr.length-1);//用finallsum来返回最终结果
System.out.println("最大子数组和为:"+finallsum);
}
}
总结:
主要是对分治思想的理解,还有归并排序的一些思想,对递归的理解等。
难度不大,注意细节就好。