Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array[−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray[4,−1,2,1]has the largest sum =6.
解析:求一个输入数组的连续子序列的最大和值。
方法一:动态规划法:
时间复杂度为O(n)。
设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。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小。
class Solution {
public:
bool invalidinput=false;
int maxSubArray(int A[], int n)
{
if(A==NULL||n<=0)
{
invalidinput=true;
return 0;
}
invalidinput=false;
int cursum=0;
int sum=0x80000000;
for(int i=0;i<n;i++)
{
if(cursum<=0)
cursum=A[i];
else
cursum+=A[i];
if(cursum>sum)
sum=cursum;
}
return sum;
}
};
方法二、分治方法
也就是递归求解。时间复杂度为O(nlogn)。这是个分治的思想,解决复杂问题我们经常使用的一种思维方法——分而治之。
而对于此题,我们把数组A[1..n]分成两个相等大小的块:A[1..n/2]和A[n/2+1..n],最大的子数组只可能出现在三种情况:
A[1..n]的最大子数组和A[1..n/2]最大子数组相同;
A[1..n]的最大子数组和A[n/2+1..n]最大子数组相同;
A[1..n]的最大子数组跨过A[1..n/2]和A[n/2+1..n]
前两种情况的求法和整体的求法是一样的,因此递归求得。
第三种,我们可以采取的方法也比较简单,沿着第n/2向左搜索,直到左边界,找到最大的和maxleft,以及沿着第n/2+1向右搜索找到最大和maxright,那么总的最大和就是maxleft+maxright。因为要跨过中间点,因此返回必须为maxleft和maxright之和!
而数组A的最大子数组和就是这三种情况中最大的一个。
int find_max_crossing(int arr[],int low,int mid,int high,int *left,int *right)
{
int lsum=-100;
int sum=0;
for(int i=mid;i>=low;--i)
{
sum+=arr[i];
if(sum>lsum)
{
lsum=sum;
*left=i;
}
}
int rsum=-100;
sum=0;
for(int j=mid+1;j<=high;j++)
{
sum+=arr[j];
if(sum>rsum)
{
rsum=sum;
*right=j;
}
}
return rsum+lsum;
}
int max_sum_array(int arr[],int low,int high,int *left,int *right)
{
if(low==high)
{//递归结束点
*left=low;
*right=high;
return arr[low];
}
int mid=(low+high)/2;
int lleft,lright,rleft,rright,mleft,mright;
//三种情况下分别求解
int lmax=max_sum_array(arr,low,mid,&lleft,&lright);
int rmax=max_sum_array(arr,mid+1,high,&rleft,&rright);
int mmax=find_max_crossing(arr,low,mid,high,&mleft,&mright);
//返回最大值
if(lmax>rmax&&lmax>mmax)
{
*left=lleft;
*right=lright;
return lmax;
}
if(rmax>lmax&&rmax>mmax)
{
*left=rleft;
*right=rright;
return rmax;
}
if(mmax>lmax&&mmax>rmax)
{
*left=mleft;
*right=mright;
return mmax;
}
}