编程之美---求数组的子数组之和的最大值

来源:互联网 发布:java web中启动线程池 编辑:程序博客网 时间:2024/06/02 00:34

问题描述:

一个有N个整数元素的一维数组(A[0],A[1],A[2],...A[n-1]),这个数组中子数组之和的最大值是多少?该子数组是连续的。例如 数组:[1,-2,3,5,-3,2]返回8; 数组:[0,-2,3,5,-1,2]返回9

问题求解:

#include <iostream>#include <malloc.h>#include <limits.h>using namespace std;//方法1:O(n^2) 遍历算法//设Sum[i,...,j]为数组a中第i个元素到第j个元素的和(0<=i<=j<n)//遍历所有的Sum[i,...,j],并且利用Sum[i,...,j]=Sum[i,....j-1]+a[j];int MaxSum1(int *a,int n){//宏INT_MIN定义类型为int的最小值,包含在头文件#include <limits.h>中    int maximum = INT_MIN;    int sum;    for(int i=0;i<n;i++)    {        sum=0;        for(int j=i;j<n;j++)        {            sum+=a[j];            if(sum>maximum)                maximum=sum;        }    }    return maximum;}//方法2:O(nlogn) 分治算法//如果将所给数组(A[0],...,A[n-1])分为长度相等的两段数组//(A[0],...,A[n/2-1])和(A[n/2],...,A[n-1]),分别求出这//两段数组各自最大子段和,则原数组(A[0],...,A[n-1])的最大子段和分//为以下三种情况:a.(A[0],...,A[n-1])的最大子段和与//(A[0],...,A[n/2-1])的最大子段和相同;//b.(A[0],...,A[n-1])的最大子段和与(A[n/2],...,A[n-1])//的最大子段和相同;c.(A[0],...,A[n-1])的最大子段跨过其中间两个//元素A[n/2-1]到A[n/2].对应a和b两个问题是规模减半的两个相同的子//问题,可以用递归求得。对于c,需要找到以A[n/2-1]结尾的和最大的一段//数组和S1=(A[i],...,A[n/2-1])和以A[n/2]开始和最大的一段和S2=//(A[n/2],...,A[j]),那么第三种情况的最大值为S1+S2。int MaxSum2(int *a, int low, int high){    if(low >= high)        return a[low];    int mid = (low+high)>>1;    int MaxLeftSum=MaxSum2(a,low,mid);//情况a:左半段最大子段和    int MaxRightSum=MaxSum2(a,mid+1,high);//情况b:右半段最大子段和    //情况c第一部分:MaxSumLeftBorder为以a[mid]结尾的最大一段数组之和    int SumLeftBorder = 0, MaxSumLeftBorder = INT_MIN;    for(int i=mid;i>=low;i--)    {        SumLeftBorder += a[i];        if(SumLeftBorder > MaxSumLeftBorder)        {            MaxSumLeftBorder = SumLeftBorder;        }    }    //情况c第二部分:MaxSumRightBorder为以a[mid + 1]    //开始的最大一段数组之和    int SumRightBorder = 0, MaxSumRightBorder = INT_MIN;    for(int i=mid+1;i<=high;i++)    {        SumRightBorder += a[i];        if(SumRightBorder > MaxSumRightBorder)        {            MaxSumRightBorder = SumRightBorder;        }    }    int Maxab=MaxLeftSum>MaxRightSum?MaxLeftSum:MaxRightSum;    //情况c:最大子段跨过中间两元素a[n/2-1]、a[n/2]的最大子段和    int Maxc=MaxSumLeftBorder + MaxSumRightBorder;    return Maxab>Maxc?Maxab:Maxc;}//方法3:动态规划法(时间复杂度为O(N))//将一个大问题(N个元素数组)转换为一个较小的问题(n-1个元素数组)。//假设result[0]为已经找到数组[0,1,...n-1]中子数组和最大的,//即保存当前找到的最大子数组。//sum[i]为包含第i个元素且和最大的连续子数组。对于数组中的第i+1个元素//有两种选择:a.作为新子数组的第一个元素//b.放入前面已经找到的最大的子数组sum[i-1]中。int MaxSum3(int *a,int n){    int *sum=(int *)malloc(n*sizeof(int));    int *result=(int *)malloc(n*sizeof(int));    //sum[i]为包含第i个元素且和最大的连续子数组    //result[i]保存当前找到的最大子数组    sum[0]=a[0];    result[0]=a[0];    for(int i=1;i<n;i++)    {//若a[i]>a[i]+sum[i-1],则作为新子数组的第一个元素,否则放    //入前面已经找到的最大的子数组sum[i-1]中        sum[i]=max(a[i],a[i]+sum[i-1]);        result[i]=max(sum[i],result[i-1]);    }    return result[n-1];}//方法4:对方法3的改进,只需O(1)的空间复杂度int MaxSum4(int a[],int n){    int sum=a[0];    int result=a[0];    for(int i=1;i<n;i++)    {        sum=max(a[i],a[i]+sum);        result=max(sum,result);    }    return result;}//方法4的变形!!!!!!!!!!!int MaxSum44(int a[],int n){    int sum=a[n-1];    int result=a[n-1];    for(int i=n-2;i>=0;i--)    {        //sum < 0时,会重置sum的值,这时相当于是一个新的子数组的起点        if(sum<0)            sum=0;        sum += a[i];        //sum > result时,更新result的值,        if(sum > result)            result=sum;    }    return result;}//扩展:返回最大子数组的位置//在sum < 0时,会重置sum的值,相当于这时是一个新的子数组的起点//在sum > result时,更新result的值,这时我们更新最大子数组的位置int MaxSum5(int a[], int n, int *start, int *end){    //sum :以当前元素开始的和最大的一段子数组    //result:存储当前所有子数组的最大值    //CurStart:存储以当前元素开始的和最大的一段子数组的开始位置    int sum=a[n-1];    int result=a[n-1];    *start=n-1;    *end=n-1;    int CurStart=n-1;    //从数组末尾往前遍历,直到数组首    for(int i=n-2;i>=0;i--)    {        if(sum < 0)        {            sum=0;            //重新标记最大和的起始位置!!!            CurStart=i;        }        sum += a[i];        if(sum > result)        {            result=sum;            //更新最大和的范围!!!            *start=i;            *end=CurStart;//从后往前遍历的原因        }    }    return result;}int main(){    int a1[6]={1,-2,3,5,-3,2};    int a2[6]={0,-2,3,5,-1,2};    int a3[5]={-9,-2,-3,-5,-3};    cout<<MaxSum1(a1,6)<<endl;    cout<<MaxSum1(a2,6)<<endl;    cout<<MaxSum1(a3,5)<<endl;    cout<<MaxSum2(a1,0,5)<<endl;    cout<<MaxSum2(a2,0,5)<<endl;    cout<<MaxSum2(a3,0,4)<<endl;    cout<<MaxSum3(a1,6)<<endl;    cout<<MaxSum3(a2,6)<<endl;    cout<<MaxSum3(a3,5)<<endl;    cout<<MaxSum4(a1,6)<<endl;    cout<<MaxSum4(a2,6)<<endl;    cout<<MaxSum4(a3,5)<<endl;    cout<<MaxSum44(a1,6)<<endl;    cout<<MaxSum44(a2,6)<<endl;    cout<<MaxSum44(a3,5)<<endl;    cout<<"**********返回最大子数组的位置**********"<<endl;    int start=0,end=0;    cout<<MaxSum5(a1,6,&start,&end)<<endl;    cout<<"start: "<<start<<" end: "<<end<<endl;    cout<<MaxSum5(a2,6,&start,&end)<<endl;    cout<<"start: "<<start<<" end: "<<end<<endl;    cout<<MaxSum5(a3,5,&start,&end)<<endl;    cout<<"start: "<<start<<" end: "<<end<<endl;    return 0;}

代码执行结果:

89-289-289-289-289-2**********返回最大子数组的位置**********8start: 2 end: 39start: 2 end: 5-2start: 1 end: 1

扩展问题:要求返回最大子数组和的位置,如何处理?
见上述代码及分析

0 0
原创粉丝点击