为什么创建调用O(h)的二叉树的递归方法的空间复杂性为何?

时间:2018-10-21 00:27:00

标签: algorithm recursion time-complexity space-complexity

使用此方法:

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?换句话说,如果每个方法调用对于输入变量n有1个存储空间,那么我们可以说对于每个方法调用,都有1个存储空间。如果有O(2 ^ n)个方法调用,那么为什么空间不等于O(2 ^ n)?我的参考文献说“在任何给定时间都只有O(N)存在”,这对我来说没有意义。

我认为堆栈框架表示方法调用,其参数和变量以及其调用者的返回地址。这可能是我困惑的根源。

1 个答案:

答案 0 :(得分:3)

很重要的一点是,这不是一个并行/并行算法,因此最终可视化计算功能输出的步骤不是同时发生的。

如果我们这样重写算法,可能会使它更明显:

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)时,将从堆栈中弹出最后一个element元素,我们将有一个大小为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)