递归函数的空间复杂性

时间:2017-04-08 18:37:50

标签: big-o space-complexity

鉴于以下功能:

Element Hello has been added, the list is : [<__main__.StringClass object at 0x1048a0cf8>, None]
Hello

我知道Big O时间复杂度为int f(int n) { if (n <= 1) { return 1; } return f(n - 1) + f(n - 1); } ,因为每次调用都会调用该函数两次。

我不明白为什么空间/内存复杂度为O(2^N)

5 个答案:

答案 0 :(得分:26)

解决这些类型问题的有效方法是考虑recursion tree。要识别的递归函数的两个特征是:

  1. 树木深度(总数 返回语句 将执行,直到基本情况为止)
  2. 树宽度(将会有多少 递归函数调用
  3. 此案例的递归关系为 C / \ / \ T(n-1) T(n-1) C ____/ \____ / \ C C / \ / \ / \ / \ T(n-2) T(n-2) T(n-2) T(n-2) 。正如你正确地注意到2^n的时间复杂度,但让我们看看它与我们的递归树有关。

    O(2^n)

    此模式将持续到我们的基本情况为止like this

    对于每个连续的树级别,我们的n减少1.因此,我们的树在到达基本情况之前将具有深度n​​ 。由于每个节点有2个分支,并且我们有n个总级别,因此我们的节点总数为O(recursion depth),使我们的时间复杂度O(n)

    我们的内存复杂性由返回语句的数量决定,因为每个函数调用都将存储在程序堆栈中。总而言之,递归函数的内存复杂度为List 1。正如我们的树深度所示,我们将有n个总返回语句,因此内存复杂度为import Foundation import RealmSwift class ShoppingList: Object{ dynamic var listName = "" var itemList = List<Item>() }

答案 1 :(得分:0)

这是我的想法:

  • 诱惑是说空间复杂度也将是O(2 ^ N),因为毕竟必须为每个O(2 ^ N)递归调用分配内存,对吗? (不正确)
  • 实际上,这些值相加/折叠,因此所需的空间只是从基本情况开始的每次调用的结果,形成数组[f(1),f (2),f(3)... f(n)],换句话说就是O(n)内存

答案 2 :(得分:0)

考虑到其他功能,可以更好地解释这一点
f(n)= f(n-1)+ f(n-2)
f(0)= 0,f(1)= 1

这将导致f(4)的以下计算树


f(4)
f(3)f(2)
f(2)f(1)f(1)f(0)
f(1)f(0)


系统可以使用等于深度的重复存储堆栈(f(0),f(1),f(2),f(3)和f(4)的存储单元来处理计算。尽管运行时需要考虑每个节点上的所有操作(加法或return语句),所以这是不影响任何节点的因素。

答案 3 :(得分:0)

我在两篇文章中找到了明确的答案。

第一次

在这个 article 处,它告诉我为什么空间复杂度是 O(n)

但我也很困惑为什么 the stack frames 一次只有 f(5) -> f(4) -> f(3) -> f(2) -> f(1) 而没有 f(5) -> f(4) -> f(3) -> f(2) -> f(0) 和其他。

The Fibonacci tree 图像:

enter image description here

然后我终于在第二篇文章中找到了答案,它清除了我的困惑。

第二个

在这 article 处很有帮助。您可以在此处查看详细信息。

The stack frames 图像: enter image description here

谢谢。

答案 4 :(得分:-1)

递归问题我们可以认为我们是用栈实现的,所以如果第一个函数调用自己第二个函数pause,它遍历到最后一个一个的加入到栈中,完成后返回并一个个remove从最顶层的堆栈,然后第二个函数恢复并遍历末尾并添加到堆栈的最顶层,并在返回时删除。但是它使用相同的堆栈,并且在同一堆栈下最多占用n个空间,因此使用空间复杂度为O(n)。