3

Given an array of integers, how can you find two indices, i and j, such that the sum of the elements in the subarray starting and ending at the indices is maximized, in linear time?

5
  • You mean - between the indices? Commented Nov 10, 2009 at 9:06
  • i = 0 and j = array.length-1 :) Commented Nov 10, 2009 at 9:08
  • 2
    @Bart, who said that array elements are greater than zero? Commented Nov 10, 2009 at 9:09
  • The answers so far seem to assume you meant "sum of the elements from index i to index j" but as far as I see you only ask for the sum of elements i and j, care to elaborate? (Also can i == j? the answer would be 2 * max(array values) in that case :-) ) Commented Nov 10, 2009 at 10:46
  • sum of the elements in between. and there are negative elements Commented Nov 10, 2009 at 17:23

4 Answers 4

11

Simple. Assume you're given the array a. First, you calculate the array s, where s[i] = a[0]+a[1]+...+a[i]. You can do it in linear time:

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

Now, the sum a[i]+a[i+1]+..+a[j] is equal to s[j]-s[i-1]. For a fixed j, to maximize the value of this difference, you should find a minimal s[i-1] in range of 0..(j-1).

Imagine a usual algorithm to find minimal value in the array.

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

You iterate and compare each array element to min... But on each iteration this min is the lowest value in array, where index range is of 0..j! And that's what we're looking for!

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;
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks to an anonymous user for his lengthy post explaining the bugs in the code. I got rid of them, except for the failure if N==0.
8

from my copy of 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)

12 Comments

A correct and a succinct one. But it took you the same time to copy it down as for me to devise it :)
Soooo, what's i and j then?
It's worth mentioning that this is actually called Kadane's algorithm; discovered (or invented, whichever floats your boat) in 1984 by Jay Kadane of Carnegie-Mellon. It is the only known linear time algorithm to solve this problem, which is more generally known as the Maximum Subarray Problem.
wont work when all the values of the input array are negative
@jillesdewit , in subsequence the elements are not necessarily contiguous. for eg: in array [1,2,3,4,5] [2,3,4] is sub array and [1,3,5] is a subsequence
|
1

this python code returns the bounds of the sequence. in terms of the original question, i=bestlo, j=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)

and here are some doctest examples that this code passes.

    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)
    '''

Comments

1

You actually need Kadane's algorithm modification that remembers lower and upper bounds for the sub-array, here's C++11 code:

#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;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.