给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
以a[0]结尾的子序列只有a[0]
以a[1]结尾的子序列有 a[0]a[1]和a[1]
以a[2]结尾的子序列有 a[0]a[1]a[2] / a[1]a[2] / a[2]
……
以a[i]结尾的子序列有a[0]a[1]……a[i-2]a[i-1]a[i] / a[1]a[2]……a[i-2]a[i-1]a[i] /
a[2]a[3]……a[i-2]a[i-1]a[i] / …… / a[i-1]a[i] / a[i]
所有以a[0] ~a[n]结尾的子序列分组构成了整个序列的所有子序列。
这样,我们只需求以a[0]~a[n]结尾的这些分组的子序列中的每一分组的最大子序列和。然后从n个分组最大子
序列和中选出整个序列的最大子序列和。
观察可以发现,0,1,2,……,n结尾的分组中,
maxsum a[0] = a[0]
maxsum a[1] = max( a[0] + a[1] ,a[1]) = max( maxsum a[0] + a[1] ,a[1])
maxsum a[2] = max( max ( a[0] + a[1] + a[2],a[1] + a[2] ),a[2])
= max( max( a[0] + a[1] ,a[1]) + a[2] , a[2])
= max( maxsum a[1] + a[2] , a[2])
……
依此类推,可以得出通用的式子。
maxsum a[i] = max( maxsum a[i-1] + a[i],a[i])
只要上一个子序列最大和为正,那么无论当前值的正负,都会与当前的相加,这样以当前值结尾的子序列最大
和就会增大。(一个正数 加一个 正数2 或者负数 那么都会比这个正数2 或负数原来要增大,同理,一个
负数加任何一个数,都会使这个数减小,因此当前一子序列最大和小于零时,我们就归零它了,相当于是不
加任何数,而保留当前位置值本身)
1.子序列必然以正数开头
2.一个子序列的前面的部分子序列之和必然为正数,若为负数就需要去掉
class Solution {
static int max;
public static int maxSubArray(int[] a) {
int[] dp = new int[a.length];
max = dp[0]=a[0];
for (int i = 1; i < a.length; i++) {
if (dp[i - 1] + a[i] > a[i]) {
dp[i] = dp[i - 1] + a[i];
}else{
dp[i] = a[i];
}
if(max<dp[i]){
max=dp[i];
}
}
return max;
}
}
另外一种思路,分治法
子序列分为 左边 右边 跨左右
static int MaxSubSum(int A[] ,int left,int right){
int MaxLeftSum,MaxRightSum;
int MaxLeftBorderSum,MaxRightBorderSum;//包含了 左边的最右边的元素 的最大值 ,右边同理
int LeftBorderSum,RightBorderSum;
if(left==right){
return A[left];
}
int mid = ( left + right ) / 2;
MaxLeftSum = MaxSubSum(A,left,mid);
MaxRightSum = MaxSubSum(A,mid+1,right);
for(int i= mid ; i>=left;i--){
LeftBorderSum +=A[i];
if(LeftBorderSum >MaxLeftBorderSum){
MaxLeftBorderSum = LeftBorderSum ;
}
}
for(int i= mid +1 ; i<=right;i++){
RightBorderSum += A[i];
if(RightBorderSum>MaxRightBorderSum){
MaxRightBorderSum= RightBorderSum;
}
}
return Math.max(Math.max(MaxLeftSum ,MaxRightSum ),MaxRightBorderSum+MaxLeftBorderSum );
}