以int数组的最大总和的子序列

时间:2009-11-10 09:04:40

标签: algorithm arrays language-agnostic

给定一个整数数组,你怎么能找到两个指数i和j,这样在索引开始和结束时子元素的总和最大化,在线性时间

4 个答案:

答案 0 :(得分:11)

简单。假设您获得了数组a。首先,计算数组s,其中s[i] = a[0]+a[1]+...+a[i]。你可以在线性时间内完成:

s[0]=a[0];
for (i=1;i<N;i++) s[i]=s[i-1]+a[i];

现在,总和a[i]+a[i+1]+..+a[j]等于s[j]-s[i-1]。对于固定的j,要最大化此差异的值,您应该在s[i-1] 的范围内找到最小0..(j-1)

想象一下通常的算法来找到数组中的最小值。

min = x[0];
for (j=1; j<N; j++)
  if (x[j] < min)
    min = x[j];

迭代并将每个数组元素与min进行比较......但在每次迭代时,此min是数组中的最小值,其中索引范围为0..j!这就是我们正在寻找的东西!

global_max = a[0];
max_i = max_j = 0;
local_min_index = 0;
for (j=1; j<N; j++){
  // here local_min is the lowest value of s[i], where 0<=i<j
  if (s[j] - s[local_min_index] > global_max) {
     global_max = s[j] - s[local_min_index]
     //update indices
     max_i = local_min_index + 1;
     max_j = j;
  }
  //update local_min_index for next iteration
  if (s[j]<local_min){
    local_min = s[j];
    // update indices
    local_min_index = j;
  }
}

答案 1 :(得分:8)

来自我programming pearls的副本:

maxsofar = 0
maxendinghere = 0
for i = [0, n)
    /* invariant: maxendinghere and maxsofar are accurate
       are accurate for x[0..i-1] */
    maxendinghere = max(maxendinghere + x[i], 0)
    maxsofar = max(maxsofar, maxendinghere)

答案 2 :(得分:1)

这个python代码返回序列的边界。就原始问题而言,i=bestloj=besthi-1

#
# given a sequence X of signed integers,
# find a contiguous subsequence that has maximal sum.
# return the lo and hi indices that bound the subsequence.
# the subsequence is X[lo:hi] (exclusive of hi).
#
def max_subseq(X):
    #
    # initialize vars to establish invariants.
    # 1: best subseq so far is [bestlo..besthi), and bestsum is its sum
    # 2: cur subseq is [curlo..curhi), and cursum is its sum
    #
    bestlo,besthi,bestsum  =  0,0,0
    curlo,curhi,cursum  =  0,0,0
    for i in xrange(len(X)):
        # extend current subseq and update vars
        curhi = i+1
        cursum += X[i]
        if cursum <= 0:
            #
            # the current subseq went under water,
            # so it can't be usefully extended.
            # start fresh at next index.
            #
            curlo = curhi
            cursum = 0
        elif cursum > bestsum:
            # adopt current subseq as the new best
            bestlo,besthi,bestsum  =  curlo,curhi,cursum

    return (bestlo,besthi)

以下是此代码通过的一些doctest示例。

    r'''
    doctest examples:
    >>> print max_subseq([])
    (0, 0)
    >>> print max_subseq([10])
    (0, 1)
    >>> print max_subseq([-1])
    (0, 0)
    >>> print max_subseq(xrange(5))
    (1, 5)
    >>> print max_subseq([-1, 1, -1])
    (1, 2)
    >>> print max_subseq([-1, -1, 1, 1, -1, -1, 1, 2, -1])
    (6, 8)
    >>> print max_subseq([-2, 11, -4, 13, -5, -2])
    (1, 4)
    >>> print max_subseq([4, -3, 5, -2, -1, 2, 6,-4])
    (0, 7)
    '''

答案 3 :(得分:1)

你实际上需要Kadane的算法修改来记住子数组的下限和上限,这里是C ++ 11代码:

#include <iostream>
#include <vector>

typedef std::pair<std::vector<int>::iterator, std::vector<int>::iterator> SubSeq;

SubSeq getMaxSubSeq(std::vector<int> &arr) {
    SubSeq maxSequence{arr.begin(), arr.begin()};
    auto tmpBegin = arr.begin();
    int maxEndingHere = 0;
    int maxSoFar = 0;

    for(auto it = arr.begin(); it < arr.end(); ++it) {
        int currentSum = maxEndingHere + *it;

        if(currentSum > 0) {
            if(maxEndingHere == 0) {
                tmpBegin = it;
            }
            maxEndingHere = currentSum;
        } else {
            maxEndingHere = 0;
        }

        if(maxEndingHere > maxSoFar) {
            maxSoFar = maxEndingHere;
            maxSequence.first = tmpBegin;
            maxSequence.second = it + 1;
        }
    }

    return maxSequence;
}

int main()
{
    std::vector<int> arr{-1, 2, 90, -50, 150, -300, 56, 12};

    auto seq = getMaxSubSeq(arr);
    while(seq.first != seq.second) {
        std::cout << *(seq.first) << " ";
        ++(seq.first);
    }

    return 0;
}