最大连续子数组
问题描述:一个整数数组,数组中有正数也有负数,一个或连续的多个整数组成一个子数组,求所有子数组的和的最大值。
求解方法;暴力求解法、动态规划法、分治法(递归)
(a) 暴力求解法就是找出所有的子数组,复杂度为O(N^2)。一般不采用。
(b) 分治法。时间复杂度为O(nlogn)。其基本思想就是将问题分解成子问题,然后递归求解子问题,直到解决我们的问题。
使用分治思想,那么就要将数组分成两个规模大致相等的子数组。就是说找到数组的中点,进行划分。划分之后会发现问题:最大连续子数组位于哪儿呢?(1)左子数组(2)右子数组(3)横跨两个子数组。连续子数组必然存在于三个地方的其中一个。对于(1)(2),其实就是原问题的子问题,只是规模小了,因此剩下的就是求解跨越中点的最大子数组。
求解跨越两个子数组的最大子数组:从中点向着两端遍历,取左右两个子数组的最大值(注意:此处的最大值并不是真的最大值,因为加了限制条件,必须包含中点),然后相加即可。
(c) 动态规划法。
设sum[i]为以第i个元素结尾且和最大的连续子数组。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,即sum[i] = max(sum[i-1]+ a[i], a[i])。(状态方程)可以通过判断sum[i-1]+ a[i]是否大于a[i]来做选择,而这实际上等价于判断sum[i-1]是否大于0。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小。
package sort;
import java.util.Arrays;
/*求解最大连续子数组
* 使用分治思想:分解,求解,合并
* */
public class LianXuZiShuZu {
public static void main(String[] args){
int[] arr = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int[] result = find_Max_SubArr( arr,0,arr.length-1);
System.out.println(Arrays.toString(result));
//动态规划
Dp(arr);
}
//分治思想求解
public static int[] find_Max_SubArr(int[] arr,int low,int high){
if(low==high){
int[] result = {low,high,arr[low]};
return result;
}
else{
int mid = (int)(low+high)/2;
int[] left_result = find_Max_SubArr(arr,low,mid);//左子数组,其子问题,递归调用
int[] right_result = find_Max_SubArr(arr,mid+1,high);//右子数组,其子问题,递归调用
int[] cross_result = find_Cross_SubArr(arr,low,mid,high);
if(left_result[2]>=right_result[2]&&left_result[2]>=cross_result[2]){
return left_result;
}
else if(right_result[2]>=left_result[2]&&right_result[2]>=cross_result[2]){
return right_result;
}
else{
return cross_result;
}
}
}
public static int[] find_Cross_SubArr(int[] arr,int low,int mid,int high){
int left_sum = Integer.MIN_VALUE;
int sum =0;
int max_left =0;
for(int i=mid;i>=low;i--){
sum = sum+arr[i];
if(sum>left_sum){
left_sum = sum;
max_left = i;
}
}
int right_sum = 0;
int max_right = 0;
sum = 0;
for(int j=mid+1;j<=high;j++){
sum = sum+arr[j];
if(sum>right_sum){
right_sum = sum;
max_right = j;
}
}
int[] result = {max_left,max_right,left_sum+right_sum};
return result;
}
//动态规划思想求解 基本思想就是:sum[i] = max(sum[i-1] + a[i], a[i])
public static void Dp(int[] arr){
int curSum = 0;
int sum = 0;
for(int i=0;i<arr.length;i++){
if(curSum<=0){
curSum = arr[i];
}
else{
curSum = curSum+arr[i];
}
if(curSum>sum){
sum = curSum;
}
}
System.out.println(sum);
}
}