递归斐波那契算法的空间复杂度是多少?

时间:2015-02-27 01:33:26

标签: java algorithm recursion time-complexity space-complexity

这是Cracking the Coding Interview(第5版)中Fibonacci序列的递归实现

int fibonacci(int i) {
       if(i == 0) return 0;
       if(i == 1) return 1;
       return fibonacci(i-1) + fibonaci(i-2);
}

在观看了该算法的时间复杂度Fibonacci Time Complexity的视频后,我现在明白为什么这个算法在O(2 n )中运行。然而,我正在努力分析空间复杂性。

我在线查看并对此有疑问。

在这个Quora主题中,作者声明“在你的情况下,你有n个堆栈帧f(n),f(n-1),f(n-2),...,f(1)和O(1)“。你不会有2n堆栈帧吗?假设为f(n-2)一帧将用于实际呼叫f(n-2)但是不会有来自f(n-1)的呼叫f(n-2)?

4 个答案:

答案 0 :(得分:20)

这是一个提示。使用print语句修改代码,如下例所示:

int fibonacci(int i, int stack) {
    printf("Fib: %d, %d\n", i, stack);
    if (i == 0) return 0;
    if (i == 1) return 1;
    return fibonacci(i - 1, stack + 1) + fibonacci(i - 2, stack + 1);
}

现在在main中执行以下行:

Fibonacci(6,1);

打印出来的“堆叠”的最高值是多少。你会看到它是“6”。尝试“i”的其他值,您将看到打印的“堆栈”值永远不会超过传入的原始“i”值。

由于Fib(i-1)在Fib(i-2)之前被完全评估,因此递归的永久性不会超过i

因此,O(N)。

答案 1 :(得分:11)

如果其他人仍然感到困惑,请务必查看此Youtube视频,该视频讨论了生成Fibonacci序列的空间复杂性。 Fibonacci Space Complexity

演示者非常清楚为什么空间复杂度为O(n),递归树的高度为n。

答案 2 :(得分:2)

正如我所看到的,该过程一次只会下降一次递归。 第一个(f(i-1))将创建N个堆栈帧,另一个(f(i-2))将创建N / 2。 所以最大的是N.另一个递归分支不会占用更多的空间。

所以我说空间复杂度是N.

事实上,在允许f(i-2)被忽略的情况下,只有一次递归被评估,因为它小于f(i-1)空间。

答案 3 :(得分:2)

递归实现的时间复杂度近似为 2 平方 n (2^n),这意味着该算法将必须经过大约 64 个计算步骤才能获得第 6 个斐波那契数。这是巨大的而不是最优的,程序需要大约 1073741824 个计算步骤才能得到 30 的斐波那契数,这是一个很小的数字,这是不可接受的。实际上,迭代方法绝对是更好和优化的方式。

recursive fibonacci calls tree

知道了这个实现的时间复杂度后,可能会认为它的空间复杂度可能是一样的,其实不然。此实现的空间复杂度等于 O(n),并且永远不会超过它。那么,让我们揭开“为什么”的神秘面纱。

这是因为递归执行的函数调用乍一看似乎是并发执行的,但实际上它们是顺序执行的。

顺序执行保证堆栈大小永远不会超过上面说明的调用树的深度。程序首先执行所有最左边的调用,然后转到右侧,当 F0 或 F1 调用返回时,它们对应的堆栈帧会被弹出。

在下图中,每个矩形代表一个函数调用,箭头代表程序到达递归结束的路径:

enter image description here

这里我们可以注意到,当程序到达调用 F4F3F2F1 并返回 1 时,它会回到其父调用 F4F3F2 执行右递归子调用 F4F3F2F0,当 F4F3F2F0 和 F4F3F2F1 调用都返回时,程序返回到 F4F3。所以栈永远不会超过最长路径F4F3F2F1的大小。

程序将遵循相同的执行模式,从父级到子级,执行左右调用后返回父级,直到到达所有 F4 的最后一个根父级,得出斐波那契数的和为 3。

>