рекурсия: массив сокращения целых чисел в двух частях равной суммы - в единственной передаче

Определенный пример, который я раньше давал студентам, - то, что они должны записать

List myList = new ArrayList(); // programming to the List interface

вместо [1 117]

ArrayList myList = new ArrayList(); // this is bad

, Они смотрят точно то же в короткой программе, но если Вы продолжаете использовать myList 100 раз в Вашей программе, можно начать видеть различие. Первое объявление гарантирует, чтобы Вы только назвали методы на myList, которые определяются List интерфейс (так никакой ArrayList определенные методы). При программировании к интерфейсу этого пути позже можно решить, что Вам действительно нужно

List myList = new TreeList();

, и только необходимо изменить код в том одном месте. Вы уже знаете, что остальная часть Вашего кода не делает ничего, что будет повреждено путем изменения реализация , потому что Вы запрограммировали к интерфейс .

преимущества еще более очевидны (я думаю), когда Вы говорите о параметрах метода и возвращаемых значениях. Возьмите это, например:

public ArrayList doSomething(HashMap map);

, Что объявление метода связывает Вас с двумя конкретными реализациями (ArrayList и HashMap). Как только тот метод называют от другого кода, любые изменения в тех типах, вероятно, означают, что Вы оказываетесь перед необходимостью изменять код вызова также. Это было бы лучше к программе к интерфейсам.

public List doSomething(Map map);

Теперь не имеет значения, какой List Вы возвращаете, или в каком Map передается в качестве параметра. Изменения, которые Вы вносите в doSomething метод, не вынудят Вас изменить код вызова.

14
задан Bill the Lizard 15 September 2012 в 23:23
поделиться

8 ответов

Вот способ сделать это, в котором используется способность 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
4
ответ дан 1 December 2019 в 16:31
поделиться

Итерация с рекурсией - тривиальное преобразование, поэтому мы предполагаем, что вы знаете, как это сделать.

Если вы используете свой «один проход» для построения собственного массива «сумма к этот индекс ", и могу сделать еще один проход по этому массиву, я мог видеть, как это сделать. Просто переберите этот второй массив и вычтите sum [x] из sum [last]. Если вы когда-нибудь обнаружите ситуацию, когда результат = сумма [x], вы вернете x. Если вы этого не сделаете, тогда верните -1. ​​

Как упоминал Нил Н., если вы очень свободно определяете «проход» для рекурсии, так что вся рекурсия может фактически посещать индексы несколько раз, тогда вы можете обойтись без второго массива.


Немного подумав над этим, я подозреваю, что идея состоит в том, чтобы заставить вас посещать каждый элемент массива только один раз (по порядку) и использовать рекурсию ' s встроенное свойство стека, чтобы избавиться от необходимости использовать второй массив.

Что вы делаете, так это пишете свою рекурсивную процедуру для сохранения текущего значения массива индекса в локальном, добавляете это значение к переданному значению "sum_of_array", а затем вызываете себя по следующему наивысшему индексу (если он есть). Если следующего по величине индекса нет, он сохраняет сумму в глобальном, который теперь доступен для каждого рекурсивного вызова с накоплением. Каждая процедура заканчивается проверкой своей суммы на общую. Если это половина, то возвращается его индекс. В противном случае возвращается -1. Если при обращении к самому себе было возвращено не -1, этот последний шаг пропускается и возвращается это значение. Я покажу на псевдо-Аде

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;

. Этот код должен иметь следующие свойства:

  • Вызов его только с массивом вернет описанный "разделенный" индекс, или -1, если нет ' t one.
  • Он считывает из любого элемента в исходном массиве только один раз
  • Он считывает элементы исходного массива в строгом порядке индекса.
  • Никакого дополнительного хранилища структурированных данных (массива) не требуется.
3
ответ дан 1 December 2019 в 16:31
поделиться
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));
0
ответ дан 1 December 2019 в 16:31
поделиться

Моя версия:

# 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, я имею в виду сумму (anArray [: 5]) == sum (anArray [5:]). «Крайние значения» также допустимы (где сумма пустого среза должна быть равна нулю), т.е. если сумма всего массива равна нулю, то 0 и len (anArray) также являются допустимыми разрезами.

0
ответ дан 1 December 2019 в 16:31
поделиться

Взгляните на следующее, используя только 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)
}
0
ответ дан 1 December 2019 в 16:31
поделиться

Вот реализация на Erlang, поскольку я изучаю ее, и это показалось мне интересной задачей. Идея беззастенчиво заимствована из решения Песто.

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.
0
ответ дан 1 December 2019 в 16:31
поделиться

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

Ваши правила несколько вводят в заблуждение. Вы требуете, чтобы никакие объекты не были размещены в куче, но IMHO нет решения, где алгоритм не имеет требований к пространству O (n) , то есть стек растет линейно с длиной списка и хвостовые вызовы невозможны, потому что функция должна проверять значения, возвращаемые рекурсивным вызовом.

0
ответ дан 1 December 2019 в 16:31
поделиться

Код в 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);
-1
ответ дан 1 December 2019 в 16:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: