求数组的子数组之和的最大值

问题来源:《编程之美》2.14 求数组的子数组之和的最大值

一个有N个整型元素的一维数组(A[0], A[1], A[2], ...,A[N-1],求这个数组的子数组之和的最大值?

首先应该明确:

1.子数组应该是连续的,即子数组中的元素在原数组中是连续的。

2.题目只要求求和,没有要求返回子数组的位置

3.整型元素数组,则数组中可能包含正整数,0,负整数

方法1:

由题知,我们可以把所有子数组的和都求出来,再比较大小,这样肯定能够得出正确解。程序描述如下:

int maxSumOfSubArr(int array[], int size)
{
    int maxsum = INT_MIN;
    int sum;
    for(int i = 0; i < size; ++i) {
        sum = 0;
        for(int j = i; j < size; ++j) {
            sum += array[j];
            if(sum > maxsum)
                maxsum = sum;
        }
    }

    return maxsum;
}

方法2:

利用动态规划算法,分析如下:

假定当前子数组的最大和存在于子数组(A[i], ..., A[j])中,我们分析遍历至A[0]元素时的子数组的最大和,此时子数组的最大和存在于下列三种情况中:

1.当 0 = i = j, 元素A[0]本身构成和最大的一段

2.当 0 = i < j, 和最大的一段以A[0]开始

3.当 0 < i,元素A[0]跟和最大的一段没有关系

于是可以得出:

Start[i] = max{A[i], A[i] + Start[i + 1]}, 这样先求出上面第1种和第2种情况的最大值

All[i] = max{Start[i], All[i + 1]},这样就求出了上面三种情况中的最大值

Start[i]中存储的是包含当前元素的子数组的和的最大值

All[i]中存储的是遍历至当前元素时,整个数组的子数组的和的最大值

int max(int x, int y)
{
    return x > y ? x : y;
}

int maxSumOfSubArr(int array[], int size)
{
    int nStart[size];
    int nAll[size];
    nStart[size - 1] = array[size - 1];
    nAll[size - 1] = array[size - 1];

    for(int i = size - 2; i >= 0; --i) {
        nStart[i] = max(nStart[i], array[i] + nStart[i+1]);
        nAll[i] = max(nStart[i], nAll[i+1]);    
    }

    return nAll[0];
}

在上面的程序描述中,我们申请了两个数组来存储中间结果,根据递推公式,用两个变量就可以代替,修改后的程序描述如下:

int maxsum(int array[], int size)
{
    int nStart = array[size - 1];
    int nAll = array[size - 1];
    
    for(int i = size - 2; i >= 0; --i) {
        nStart = max(array[i], nStart + array[i]);
        nAll = max(nStart, nAll);
    }

    return nAll;
}

也可写成如下形式:

int maxSum(int array[], int size)
{
    int nStart = array[size - 1];
    int nAll = array[size - 1];

    for(int i = size - 2; i >= 0; --i) {
        if(nStart < 0)
            nStart = 0;
        nStart += array[i];
        if(nStart > nAll)
            nAll = nStart;
    }

    return nAll;
}

现在如果对题目中增加一个要求,返回最大子数组的位置,怎么做?

从上面的程序描述中可以看出,在nStart < 0时,会重置nStart的值,相当于这时是一个新的子数组的起点,在nStart > nAll时,更新nAll的值,这时我们更新最大子数组的位置。程序描述如下:

//|*lIndex|, |*rIndex|用来存储和最大的子数组的位置
int maxSum(int array[], int size, int *lIndex, int *rIndex)
{
    assert(array != NULL && size != 0);

    //|nStart|和最大的一段子数组以当前元素开始
    //|nAll|存储当前所有子数组的最大值
    //|rStart|存储以当前元素开始的和最大的一段子数组的开始位置
    int nStart = array[size - 1];
    int nAll = array[size - 1];

    *lIndex = size - 1;
    *rIndex = size - 1;
    int rStart = size - 1;

    //从数组末尾往前遍历,直到数组首
    for(int i = size - 2; i >= 0; --i) {
        if(nStart < 0) {
            nStart = 0;

            //重新标记最大和的起点
            rStart = i;
        }

        nStart += array[i];
        if(nStart > nAll) {
            nAll = nStart;

            //更新最大和的范围
            *lIndex = i;
            *rIndex = rStart;
        }
    }

    return nAll;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值