递归:在两个相等和的部分中切割整数数组 - 在一次传递中

时间:2009-07-15 16:06:18

标签: algorithm puzzle

使用递归,找到一个将数组分成两部分的索引,以便两个部分具有相等的总和。

切割意味着用刀切割。索引< =到结果的所有单元的总和必须等于索引>>的所有单元。结果。没有细胞可以被遗弃或成为双方的一部分。

数组包含任意整数(即正数,负数和零)。

如果没有这样的索引返回-1

不允许分配堆对象。

你必须一次性完成。

你必须使用递归(即不能使用循环结构)。

可以是任何语言或伪代码。

忘记添加:您无法修改数组

8 个答案:

答案 0 :(得分:4)

这是一种利用Ruby能够返回多个值的方法。第一个值是拆分的索引(如果存在),第二个值是每一半的总和(如果没有找到拆分,则为整个数组的总和):

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

这样称呼:

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

结果如下:

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

编辑:


这是一个略微修改的版本,以解决onebyone.livejournal.com的评论。现在,数组中的每个索引只能访问一次:

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

答案 1 :(得分:3)

使用递归迭代是一个微不足道的转换,所以我们假设你知道如何做到这一点。

如果你使用“一次通过”构建自己的“总和到这个索引”的数组,并且可以在该数组上再次传递,我可以看到如何做到这一点。只需迭代第二个数组并从sum [last]中减去sum [x]。如果您发现结果= sum [x]的情况,则返回x。如果不这样做,则返回-1。

正如Neil N所提到的,如果你为递归定义“传递”非常松散,这样整个递归实际上可以多次访问索引,那么你可以省去第二个数组。


在考虑了这一点之后,我怀疑这个想法是让你只访问每个数组元素一次(按顺序),并使用递归的内置堆栈属性来摆脱任何第二个数组的需要。

你要做的是写你的递归例程来保存它在本地的当前索引的数组值,将该值添加到传入的“sum_of_array”值,然后在下一个最高索引(如果有的话)上调用自己。如果没有下一个最高索引,它会将总和保存到全局,现在可用于每个堆叠递归调用。每个例程通过检查其总和与全局总和来完成。如果是一半,则返回其索引。否则返回-1。如果从对自身的调用返回非-1,则跳过最后一步并返回该值。我将在伪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;

此代码应具有以下属性:

  • 仅使用数组调用它将返回描述的“split”索引,如果没有,则返回-1。
  • 它只能从源数组中的任何元素读取一次
  • 它以严格的索引顺序读取源数组元素。
  • 不需要额外的结构化数据存储(阵列)。

答案 2 :(得分: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);
    }
}

该方法如下调用。

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

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

这可以减少为仅使用一个总和并且定位为零。

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]);
    }
}

现在调用该方法如下。

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

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

答案 3 :(得分:0)

看一下下面的内容,只使用1个索引,假设数组的索引是从1开始的:

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

答案 4 :(得分:0)

我的版本:

# 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

注意:如果返回的索引是5,我的意思是sum(anArray [:5])== sum(anArray [5:])。 “极值”也是有效的(其中空切片的总和意味着为零),即如果整个数组的总和为零,则0和len(anArray)也是有效切割。

答案 5 :(得分:0)

这是Erlang中的一个实现,因为我正在学习它,这似乎是一个有趣的挑战。从Pesto的解决方案中无耻地贬低了想法。

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.

答案 6 :(得分:0)

哈斯克尔:

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

你的规则有些误导。你需要在堆上没有分配任何对象,但恕我直言,没有解决方案,其中算法没有O(n)的空间要求,即堆栈与列表的长度线性增长,尾部调用不是可能是因为函数必须检查递归调用的返回值。

答案 7 :(得分:-1)

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

使用以下语法调用:

cut(0, array.length, 0, 0, array);