14

Using recursion, find an index that cuts an array in two parts so that both parts have equal sum.

Cut means to cut like with a knife. All the cells with index <= to the result must be equal in their sum to the all the cells with index > to the result. No cells can be left off or be part of both sides.

The arrays contains arbitrary integers (i.e. positives, negatives, and zeros).

If there is no such index return -1.

You are not allowed to allocate heap objects.

You must do it in a single pass.

You must do it with recursion (i.e. cannot use loop constructs).

Can be in any language or pseudocode.

Forgot to add this: You cannot modify the array

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
flybywire
  • 261,858
  • 191
  • 397
  • 503
  • 4
    How do you define a "single pass" in recursion? Do you mean each node will only be touched once? – Neil N Jul 15 '09 at 16:08
  • yes, each node can only be read once (the array is RO, see the updated version of the question) – flybywire Jul 15 '09 at 16:10
  • what if the total of the array is odd? or is it assumed that it won't be? – Victor Jul 15 '09 at 16:11
  • @bill: arbitrary integers (positives / negatives / zeros) – flybywire Jul 15 '09 at 16:11
  • @victor can be odd. for example this array: [1, 1, 2] has odd number of elements and has a solution – flybywire Jul 15 '09 at 16:12
  • @Nosredna See the question ... "If there is no such index return -1." – Daniel Brückner Jul 15 '09 at 16:59
  • Ah, I see now. I was just interpreting Victor's question. – Nosredna Jul 15 '09 at 17:04
  • yeah... overlooked that that would mean 'no such index'. I found a solution right away with java but involved reading elements more than once. :( still O(N) tho. – Victor Jul 15 '09 at 17:36
  • A couple of test vectors: f([1,2,3]) = 2, f([1,2,3,4]) = -1, f([1,2,3,4,4,6]) = 4 (all assuming zero based indexing, and that the returned index should be the first element of the right side of the array. – Nick Johnson Jul 15 '09 at 19:48
  • @victor, if you read elements more than once then it is trivial: first compute the sum of the array and then advance one by one until the partial sum is half the total. – flybywire Jul 16 '09 at 07:12

8 Answers8

4

Here's a way to do it that takes advantage of Ruby's ability to return multiple values. The first value is the index for the split (if it exists), the second is the sum of each half (or the sum of the whole array if no split is found):

def split(arr, index = 0, sum = 0)
  return -1, arr[index] if index == arr.length - 1
  sum = sum + arr[index]
  i, tail = split(arr, index + 1, sum)

  if i > -1
    return i, tail
  elsif sum == tail
    return index, sum 
  end
  return -1, arr[index] + tail
end

Calling it like this:

p split([1, 1, 2])
p split([1])
p split([-1, 2, 1])
p split([2, 3, 4])
p split([0, 5, 4, -9])

Results in this:

[1, 2]
[-1, 1]
[1, 1]
[-1, 9]
[0, 0]

EDIT:


Here's a slightly modified version to address onebyone.livejournal.com's comments. Now each index in the array is accessed only once:

def split(arr, index = 0, sum = 0)
  curr = arr[index]
  return -1, curr if index == arr.length - 1
  sum = sum + curr
  i, tail = split(arr, index + 1, sum)

  if i > -1
    return i, tail
  elsif sum == tail
    return index, sum 
  end
  return -1, curr + tail
end
Pesto
  • 23,810
  • 2
  • 71
  • 76
  • works! and be proud of spreading the gospel of ruby, I installed it just to test your answer. (will I ever use it again?) – flybywire Jul 15 '09 at 16:50
  • 1
    While it is quite trival to do with multiple return values, I wonder if and how it can be done using only a single integer as return value. +1 – Daniel Brückner Jul 15 '09 at 16:57
  • @Daniel +1 I asked myself the same question. I don't see an answer short of "encoding" two integers into one. Perhaps you want to ask another question in SO? If so please put a link here. – flybywire Jul 15 '09 at 17:01
  • @onebyone there is something of truth in what you say. I picked this answer because it is in code (as opposed to pseudo code) and could be easily tested). – flybywire Jul 15 '09 at 17:04
  • Once we had the following question here on SO. http://stackoverflow.com/questions/731832/interview-question-ffn-n/731857#731857 I could imagine that a similar trick could help. – Daniel Brückner Jul 15 '09 at 17:05
  • @onebyone: A fair point. I've edited the answer to reflect this. – Pesto Jul 15 '09 at 17:24
3

Iterating with recursion is a trivial transformation, so we'll assume you know how to do that.

If you use your "one pass" to build your own array of "sum to this index", and can make another pass on that array, I could see how to do it. Just iterate through that second array and subtract sum[x] from sum[last]. If you ever find a situation where the result = sum[x] you return x. If you don't then return -1.

As Neil N mentioned, if you define "pass" very loosely for recursion, such that the entire recursion can actually visit indices multiple times, then you could dispense with the second array.


After thinking about this a bit, I suspect the idea is to get you to only visit every array element once (in order), and to use recursion's built-in stack property to get rid of the need for any second array.

What you do is write your recursive routine to save off it's current index's array value in a local, add that value to a passed in "sum_of_array" value, then call itself on the next highest index (if there is one). If there isn't a next highest index, it saves the sum into a global, which is now available to every stacked recursive call. Each routine finishes by checking its sum against the global sum. If it is half, then it returns its index. Otherwise it returns -1. If a non -1 was returned from a call to itself, this last step is skipped and that value is returned. I'll show in pseudo-Ada

Total_Sum : integer;

function Split (Subject : Integer_Array; After : Integer := 0; Running_Sum : Integer := 0) is
begin
   Running_Sum := Running_Sum + Subject(After);
   if (After < Subject'last) then --'// comment Hack for SO colorizer
      Magic_Index : constant Integer := Split (Subject, After +  1, Running_Sum);
      if (Magic_Index = -1) then
         if (Total_Sum - Running_Sum = Running_Sum) then
            return After;
         else
            return -1;
         end if;
      else
         return Magic_Index;
      end if;
   else
      Total_Sum := Running_Sum;
      return -1;
   end if;
end Split;

This code should have the properties that:

  • Calling it with just an array will return the described "split" index, or -1 if there isn't one.
  • It only reads from any element in the source array once
  • It reads the source array elements in strict index order.
  • No extra structured data storage (array) is required.
T.E.D.
  • 44,016
  • 10
  • 73
  • 134
0
public static Int32 SplitIndex(Int32[] array, Int32 left, Int32 right, Int32 leftsum, Int32 rightsum)
{
    if (left == right - 1)
    {
        return (leftsum == rightsum) ? left : -1;
    }

    if (leftsum > rightsum)
    {
        return SplitIndex(array, left, right - 1, leftsum, rightsum + array[right - 1]);
    }
    else
    {
        return SplitIndex(array, left + 1, right, leftsum + array[left + 1], rightsum);
    }
}

The method is called as follows.

Int32[] a = { 1, 2, 3, 1, 6, 1 };

Console.WriteLine(SplitIndex(a, -1, a.Length, 0, 0));

This can be reduced to use only a single sum and targeting zero.

public static Int32 SplitIndex(Int32[] array, Int32 left, Int32 right, Int32 sum)
{
    if (left == right - 1)
    {
        return (sum == 0) ? left : -1;
    }

    if (sum > 0)
    {
        return SplitIndex(array, left, right - 1, sum - array[right - 1]);
    }
    else
    {
        return SplitIndex(array, left + 1, right, sum + array[left + 1]);
    }
}

The method is now called as follows.

Int32[] a = { 1, 2, 3, 1, 6, 1 };

Console.WriteLine(SplitIndex(a, -1, a.Length, 0));
Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
0

Take a look at the following, using only 1 index, assume array's indexes are 1-based:

int recursion(index, rightvalue, leftvalue, array)
{
if array=[] then
{
    if rightvalue=leftvalue then return index
    else return -1
}
else
{
    if rightvalue <= leftvalue
    { recursion(index+1, rightvalue+array[1], leftvalue, array[2..len(array)] }
    else 
    { recursion(index, rightvalue, leftvalue+array[len(array)], array[1..len(array)-1] }
}

int main_function(array)
{
    return recursion(1, 0, 0, array)
}
Roee Adler
  • 33,434
  • 32
  • 105
  • 133
0

My version:

# Returns either (right sum from the currentIndex, currentIndex, False),
# or, if the winning cut is found, (sum from the cut, its index, True)
def tryCut(anArray, currentIndex, currentLeftSum):
   if currentIndex == len(anArray):
      return (0, currentIndex, currentLeftSum==0)

   (nextRightSum, anIndex, isItTheWinner) = tryCut(anArray, currentIndex + 1, currentLeftSum + anArray[currentIndex])

   if isItTheWinner: return (nextRightSum, anIndex, isItTheWinner)
   rightSum = anArray[currentIndex] + nextRightSum
   return (rightSum, currentIndex, currentLeftSum == rightSum)

def findCut(anArray):
   (dummy, anIndex, isItTheWinner) = tryCut(anArray, 0, 0)
   if isItTheWinner: return anIndex
   return -1

Note: if the index returned is 5, I mean that sum(anArray[:5]) == sum(anArray[5:]). The "extremes" are also valid (where the sum of an empty slice is meant to be zero), i.e. if the sum of the whole array is zero, then 0 and len(anArray) are also valid cuts.

Federico A. Ramponi
  • 46,145
  • 29
  • 109
  • 133
0

Here's an implementation in Erlang, since I'm learning it and this seemed like an interesting challenge. Idea shamelessly cribbed from Pesto's solution.

find_split(List) -> {Idx, _Sum} = find_split(List, 1, 0), Idx.
find_split([Head], _Idx, _Sum) -> {-1, Head};
find_split([Head|Tail], Idx, Sum) ->
  case find_split(Tail, Idx + 1, Sum + Head) of
    {-1, Tailsum} when Sum + Head == Tailsum -> {Idx, Sum + Head};
    {-1, Tailsum} -> {-1, Head + Tailsum};
    Ret -> Ret
  end.
Nick Johnson
  • 100,655
  • 16
  • 128
  • 198
0

Haskell:

split' _ s [] = (-1, s)
split' idx s (x:xs) | sidx >= 0   = (sidx, s')
                    | s * 2 == s' = (idx - 1, s)
                    | otherwise   = (-1, s')
    where (sidx, s') = split' (idx + 1) (x + s) xs

split = fst . split' 0 0

Your rules are somewhat misleading. You require that no objects are to be allocated on the heap, but IMHO there is no solution where the algorithm does not have space requirements of O(n), i.e. the stack grows linearly with the length of the list and tail calls are not possible because the function has to inspect the return values from the recursive call.

Torsten Marek
  • 83,780
  • 21
  • 91
  • 98
-1

Code in C/C++/Java:

function cut(int i, int j, int s1, int s2, int a[])
{
    if(i==j && s1==s2)
        return i;
    else if(i==j && s1!=s2)
        return -1;
    else if(s1>s2)
        return cut(i, j-1, s1, s2 + a[j-1]);
    else
        return cut(i+1, j, s1 + a[i+1], s2);
}

Call using the following syntax:

cut(0, array.length, 0, 0, array);
Niyaz
  • 53,943
  • 55
  • 151
  • 182
  • for me this is returning -1 with arrays it shouldn't be... though i am porting this into java. anything i'm getting wrong? i'm replacing function with 'public static int' then adding ,a to the recursive calls. – Victor Jul 15 '09 at 17:46
  • You can fix the code by replacing s1 + a[i+1] by s1+a[i], then it works for some cases. However, there are pathological cases where it breaks, like [3, -3, -3, 3]. This array has a split at 1, but the code will return -1. – Torsten Marek Jul 15 '09 at 21:43