分治策略--最大子数组问题

在分治策略中,我们递归地求解一个问题,在每层递归中应用如下三个步骤:

分解步骤将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更少。

解决步骤递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解。

合并步骤将子问题的解组合成原问题的解。

最大子数组问题

问题描述:某家公司的股票价格是不稳定的,你可以在某个时刻买进一股该公司的股票,并在之后将其卖出,买进卖出都是在当天交易结束后进行。你知道股票的将来价格。你的目标是最大化收益。即“低价买进,高价卖出”。

        股票价格:A[] = {100,113,110,85,105,102,86,63,81,101,94,106,101,79,94,90,97}; 

暴力求解方法

  可以直接选择两个日期的股票价格组合,有n(n-1)/2可能的组合,其时间复杂度为n^2。

分治策略

我们的目的是寻找一段日期,使得从第一天到最后一天的股票价格净变值最大。我们可以不从每日价格的角度去看待输入数据,而是考察每日价格变化,第i天的价格变化定义为第i天和第i-1天的价格差,故可以得到价格变化:

        价格变化 B[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};

那么问题就转化为寻找B的和最大的非空连续子数组,即最大子数组。

使用分治策略求解,首先我们就需要把原问题分解成子问题,也就是子数组,使用分治技术可以将子数组划分为两个规模尽量相等的子数组,就是从数组中央分解。

第二步当分解到足够小,即只有一个元素的子数组,那么直接返回其值。

第三步合并:关于数组A的任何最大连续子数组A[i..j]所处的位置必然是以下三种情况:

1.完全位于数组左边A[low..mid]中,因此low<=j<=j<=mid,

2.完全位于子数组右边A[mid+1..high]中,因此mid<i<=j<=high,

3.跨越了中点,因此low<=i<=j<=high,

我们可以递归地求解A[low..mid]和A[mid+1..high]的最大子数组,因为这两个子问题依旧是最大子数组问题,只是规模更小.。剩下的是寻找跨越中点的最大子数组,然后在三种情况中选择最大的。此问题并非是比原问题更小规模的实例,因为它加入了限制---求出的子数组必须跨越中点。可以在线性时间内完成。我们只需找出形如A[low..mid]和A[mid+1..high]的最大子数组,然后将其合并即可。

int findMaxCrossingSubarray(int A[],int low,int mid,int high)
{
    int leftSum = -999999;
    int sum = 0;
    for(int i = mid;i>=low;i--)
    {
        sum += A[i];
        if(sum > leftSum)
        {
            leftSum = sum;
        }
    }
    int rightSum = -99999;
    sum = 0;
    for(int i= mid;i<=high; i++)
    {
        sum += A[i];
        if(sum > rightSum){
            rightSum = sum;
        }
    }
    return leftSum + rightSum;
}

递归代码如下:

int findMaximumSubarray(int A[],int low,int high)
{
    if(high == low)
        return A[low];
    else {

        int mid = (low + high)/2;
        int maxSum = -999999;

        int leftSum = findMaximumSubarray(A,low,mid);
        if(leftSum > maxSum)
            maxSum = leftSum;

        int rightSum = findMaximumSubarray(A,mid+1,high);
        if(rightSum > maxSum)
            maxSum = rightSum;

        int crossSum = findMaxCrossingSubarray(A,low,mid,high);
        if(crossSum > maxSum)
            maxSum = crossSum;

        return maxSum;
    }
}

其整体时间复杂度为nlogn;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木羊子羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值