最大子序列问题的解

最大子序列问题的解,就是求一个数组中,从某个位置开始到某个位置结束,这段数相加得到的和是该数组中最大的值,求这个最大值。
举个例子:4,-3,5,-2,-1,2,6,-2这8个数的数组中,最大的子序列的和为11,从A1到A7相加为最大值。

求解这个问题有多种方法,这里介绍三种方法来求解该问题。

第一种也就是蛮力法,从数组头开始遍历,得到所有的可能,然后一个个进行比较,得到最大值。

int get_max(const vector<int> &a)
{
/*max_num表示最大值,this_num表示当前的和*/
    int max_num=0;
    for(int i=0;i<a.size();i++)//标示序列的开始位置
    {
        int this_num=0;
        for(int j=i;j<a.size();j++)/*得到从j位置开始的最大子序列*/
        {
            this_num+=a[j];

            if(this_num>max_num)/*比较当前的值是否大于以前的最大值*/
                max_num=this_num;
        }
    }
    return max_num;
}

思想简单,代码简单,但是其时间复杂度却是N的平方。

第二种方法使用的就是分治法思想,使用递归的方式来解决问题。

我们思考最大子序列出现的位置,如果我们将整个数组一分为二,可以发现一个规律,那就是最大子序列只会出现在三个位置——数组左边,数组右边,或者在连接两边的中间位置。
只要我们能够求出左边的最大子序列的值,右边最大子序列的值,以及中间部分的最大子序列的值,三者比较,最大者就是我们所需要的最大子序列的值。

数组左边和数组右边的最大值很好解决,但是跨越两边是什么情况呢?
这种情况就是左边的最大值和右边的最大值相加就能得到我们需要的中间部分的最大值。
当然,问题肯定不是这么简单,这个左边的最大值和右边的最大值并不等于我们前面说的左边的最大子序列和右边的最大子序列。
最大子序列要求我们所有的数都必须是相连的,所以这种情况下的左边最大值的子序列必须和右边最大值的子序列相连接起来,即必须包含数组中间的两个数。
接着我们的开始的例子,先分成两边,左边为4,-3,5,-2,左边的最大子序列很简单A1到A3为6,右边为-1,2,6,-2,右边的最大子序列A5到A7为7。
最后是中间的最大子序列,必须是包含两边相连的最大子序列的值相加,也就是说左边必须包含有-2,右边必须包含有-1,这样,两边的序列才能接起来,构成子序列。左边包含-2的最大子序列为4,A1到A4,右边包含-1的最大子序列为A5-A7为7,两件相加为11。
三个数进行比较,我们发现最大子序列出现在中间,为11,从A1到A7。

下面是代码:

int max(int a,int b,int c)/*得到三个数的最大值
{
    int n=a>b?a:b;
    return n>c?n:c;
}

下面函数为求得中间部分的最大子序列的值。
参数s代表数组的开始下标,left代表左边部分最后一个数的下标,right代表右边第一个数的下标,e代表数组的最后下标.
求得的方法和第一种方法没有什么差别。

int middle_num(const vector<int> & a,int s,int left,int right,int e)
{
    int left_max=0,this_max=0;
    for(int i=left;i>=s;i--)//求得左边的最大子序列(包含a[left])
    {
        this_max+=a[i];
        if(this_max>left_max)
            left_max=this_max;
    }

    int right_max=0;
    this_max=0;
    for(int i=right;i<=e;i++)//求得左边的最大子序列(包含a[right])
    {
        this_max+=a[i];
        if(this_max>right_max)
            right_max=this_max;
    }

    return left_max+right_max;
}
/*得到最大子序列的值*/
int get_max(const vector<int> & a,int s,int e)
{
    if(s==e)
        return a[s];
    int len=(e-s)/2;

    int left_max=get_max(a,s,s+len);//左边最大值
    int right_max=get_max(a,s+len+1,e);//右边最大值
    int middle_max=middle_num(a,s,s+len,s+len+1,e);/*中间最大值*/

    return max(left_max,right_max,middle_max);/*三者中最大的*/
}

使用递归的思想来解决问题,思想有点发杂,代码相比第一种方法多了很多,但是它的时间复杂度确确实实比第一种方法好,为O(NlogN),降低了很多。

第三种方法最为简单,时间复杂度仅仅只有O(N),下面为代码:

int get_max(const vector<int> & a)
{
    int max_num=0,this_num=0;
    for(int i=0;i<a.size();i++)
    {
        this_num+=a[i];
        if(this_num>max_num)
            max_num=this_num;
        else if(this_num<0)/*这里是重点*/
            this_num=0;
    }
    return max_num;
}

这种方法最为简单,代码更是简单。

这种方法可不可以,还有思想是什么,请自己去想。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值