Почему пространственная сложность рекурсивного метода, который создает двоичное дерево вызовов O(h)?

Учитывая этот метод:

int f(int n) {
  if (n <= 0) {
     return 1;
  }
  return f(n - 1) + f(n - 1);
}

Создает стек стека / двоичное дерево следующим образом:

      n
     /  \
   n-1   n-1
 /   \  /   \
n-2 n-2 n-2 n-2 etc

например. n = 3

      3
     /  \
   2     2
  / \    / \
 1   1   1   1
/ \ / \ / \ / \
0 0 0 0 0 0 0 0 

2 ^ (n + 1) - 1 => 15 узлов / вызовов. Сложность времени O(2^n)

Мой вопрос: почему сложность пространства = O(h), h представляет высоту дерева, которая в примере равна 3? Другими словами, если каждый вызов метода имеет 1 пространство памяти для входной переменной n, то мы можем сказать, что для каждого вызова метода есть 1 пространство памяти. Если есть вызовы методов O(2^n), то почему пространство не равно O(2^n)? Моя ссылка говорит, что "только O(N) существует в любой момент времени", что не имело смысла для меня.

Я думаю о стековом фрейме как о представлении вызова метода, его параметров и переменных и адреса возврата его вызывающей стороны. Это может быть корнем моего замешательства.

1 ответ

Решение

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

Если мы переписаем алгоритм так, это может сделать его более очевидным:

int f(int n) {
  if (n <= 0) {
     return 1;
  }

  int temp1 = f(n - 1);
  int temp2 = f(n - 1);
  return temp1 + temp2;
}

Так что звонки на первый f(n - 1) а второй f(n - 1) не происходят одновременно.

Это означает, что в любой момент времени у нас есть линейный стек вызовов, подобный этому:

      f(3)
     / 
    f(2)   
   /  
  f(1) 
 /
f(0)

На данный момент у нас есть стек вызовов размером 4. Когда f(0) вычисляется, последний элемент элемента извлекается из стека, и у нас будет стек вызовов размером 3:

      f(3)
     / 
    f(2)   
   /  
  f(1)

В этот момент алгоритм оценивает второй вызов f(1) (правильное поддерево f(1)):

      f(3)
     / 
    f(2)   
   /  
  f(1)
  \
   f(0)

У нас снова есть стек вызовов размером 4. На следующих нескольких шагах стек вызовов преобразуется в:

      f(3)
     / 
    f(2)   
   /  
  f(1)

а потом:

      f(3)
     / 
    f(2)   

а потом:

      f(3)
     / 
    f(2)
    \
     f(1)

а потом:

      f(3)
     / 
    f(2)
    \
     f(1)
    /
   f(0)

а потом:

      f(3)
     / 
    f(2)
    \
     f(1)

а потом:

      f(3)
     / 
    f(2)
    \
     f(1)
      \
       f(0)

и этот процесс продолжается, пока алгоритм не закончится.

Следовательно, можно сделать вывод, что сложность пространства для алгоритма равна O (h).

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