Сложности во время выполнения для рекурсивных алгоритмов

Я искал высоко и низко и не могу найти много материала, связанного со сложностями во время выполнения, рекурсией и Java.

В настоящее время я изучаю сложности времени выполнения и нотацию Big-O в моем классе Algorithms, и у меня возникают проблемы с анализом рекурсивных алгоритмов.

private String toStringRec(DNode d)
{
   if (d == trailer)
      return "";
   else
      return d.getElement() + toStringRec(d.getNext());
}

Это рекурсивный метод, который просто выполняет итерацию по двусвязному списку и распечатывает элементы.

Единственное, что я могу придумать, это то, что он имеет сложность во время выполнения O(n), так как количество рекурсивных вызовов метода будет зависеть от количества узлов в DList, но я все еще не чувствую себя комфортно с этот ответ

Я не уверен, должен ли я учитывать добавление d а также d.getNext(),

Или я просто не в курсе, а сложность во время выполнения постоянна, так как все, что она делает, это извлекает элементы из DNodes в DList?

5 ответов

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

Однако не все так просто: сложность заключается в добавлении d.getElement() на растущую строку: это само по себе линейная операция, и она повторяется N раз. Следовательно, сложность вашей функции O(N^2),

Если T(n) - это число элементарных операций (в данном случае - когда мы вводим тело функции, любая из строк внутри выполняется не более одного раза, а все, кроме второго возврата, не выполняются O(1)) вызывая toStringRec для списка из n элементов, затем

  T(0) = 1  - as the only things that happen is the branch instruction and a
              return
  T(n) = n + T(n-1) for n > 0 - as the stuff which is being done in the
              function besides calling toStringRec is some constant time stuff and
              concatenating strings that takes O(n) time; and we also run
              toStringRec(d.getNet()) which takes T(n-1) time

На данный момент мы описали сложность алгоритма. Теперь мы можем вычислить замкнутую форму для T, T(n) = O(n**2).

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

T(0) = 0
T(n) = C + T(n-1)

Затем вы можете найти время выполнения, используя подстановку, чтобы найти ряд:

T(n) = C + T(n-1) = 2C + T(n-2) = 3C + T(n-3) = ... = nC + T(n-n) = nC + 0 = nC

По определению O это уравнение O(n). Этот пример не особенно интересен, но если вы посмотрите на что-то вроде времени выполнения сортировки слиянием или другого алгоритма "разделяй и властвуй", вы можете получить лучшее представление о рекуррентных отношениях.

Алгоритм имеет сложность O(n) во время выполнения, как вы предлагаете. В вашем списке есть n элементов, и алгоритм будет выполнять почти фиксированный объем работы для каждого элемента (эта функция включает доступ к элементам и следующим элементам, а также новый вызов toStringRec). Извлечение элемента из DNode занимает постоянное время, а постоянное время отбрасывается в нотации big-O.

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

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

T (n) = T (n - 1) + O (1)

(Предположим, что функция getElement работает в постоянное время.) Решение этого уравнения тривиально T(n) = O(n).

Это был общий случай. Однако иногда вы можете проанализировать алгоритм без написания такого уравнения. В этом примере вы можете легко утверждать, что к каждому элементу обращаются максимум один раз, и каждый раз выполняется некоторая постоянная работа; Таким образом, для выполнения работы требуется O (n).

Другие вопросы по тегам