使用此方法:
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)存在”,这对我来说没有意义。
我认为堆栈框架表示方法调用,其参数和变量以及其调用者的返回地址。这可能是我困惑的根源。
答案 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)。