web-dev-qa-db-ja.com

再帰関数の空間の複雑さ

以下の関数を考えます:

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

Big Oの時間の複雑さはO(2^N)であることを知っています。これは、各呼び出しが関数を2回呼び出すためです。

私が理解していないのは、スペース/メモリの複雑さがO(N)なのはなぜですか?

21
George Kagan

これらのタイプの問題にアプローチする便利な方法は、 再帰ツリー を考えることです。識別する再帰関数の2つの機能は次のとおりです。

  1. ツリーの深さ(合計returnステートメントが基本ケースまで実行される回数)
  2. ツリーの幅(合計再帰関数呼び出しの数)

この場合の再帰関係はT(n) = 2T(n-1)です。 O(2^n)の場合は時間の複雑さを正しく指摘しましたが、繰り返しツリーに関連して見てみましょう。

_      C
     / \         
    /   \      
T(n-1)  T(n-1)

            C
       ____/ \____
      /           \
    C              C   
   /  \           /  \
  /    \         /    \
T(n-2) T(n-2) T(n-2)  T(n-2)
_

このパターンは、 like this に見えるベースケースまで続きます。

連続するツリーレベルごとに、nは1ずつ減少します。したがって、ツリーはベースケースに到達する前にの深さnを持ちます。各ノードには2つのブランチがあり、合計レベルはnなので、ノードの合計数は_2^n_であり、時間の複雑さはO(2^n)です。

各関数呼び出しはプログラムスタックに格納されるため、メモリの複雑さはreturnステートメントの数によって決まります。一般化すると、再帰関数のメモリの複雑さはO(recursion depth)です。ツリーの深さが示唆するように、合計returnステートメントがn個あるため、メモリの複雑さはO(n)です。

32
Ritwik Biswas