递归函数的空间复杂度是否最小为O(N)?

时间:2013-11-17 07:37:15

标签: c++ c algorithm recursion big-o

我在考虑递归函数。采用一个简单的函数,例如一个递归打印链表:

void print(list *list){
  if(list){
     cout << list->data
     print(list->next);
  }
}

首先,这似乎是一个非常无害的功能,但它不是在每个堆栈帧中存储一个地址(由变量列表标记)?假设没有尾调用优化。我们需要N个地址的空间,其中N是列表的大小。所需空间与列表大小成比例地线性增长。

我想不出如何在没有至少一个局部变量或参数存储在堆栈中的情况下实现递归函数。因此,似乎每个递归函数都具有最佳线性空间复杂度。如果是这种情况,那么在递归时使用迭代几乎总是不可取吗?

4 个答案:

答案 0 :(得分:9)

虽然可以假设所有非优化函数调用都使用堆栈帧,但并非总是在N个元素上运行的递归算法需要堆栈O(N)。

递归树遍历算法使用O(lg N)堆栈帧,例如,递归QuickSort。

答案 1 :(得分:2)

你是对的,假设没有尾调用优化,代码片段的空间复杂度在列表大小上是线性的。

一般情况下,递归会让事情变得更慢,更多的内存更多,是的。但是你不能总是通过迭代实现它来渐进地改进它,因为对于非尾递归函数,你需要在迭代实现中手动维护堆栈,所以你仍然会使用相同数量的内存。

想一想深度优先遍历。您需要将每个节点存储在堆栈中,以及下一个需要访问的子节点,以便在您访问其中一个子节点后返回后,您将知道要转到下一个节点。递归使这很容易,因为它抽象了所有丑陋的簿记。迭代实现不会渐近更好,我希望实际差异也非常非常小。

很多时候,递归会让事情变得更轻松而不会牺牲任何东西。在你的情况下,它没有意义 - 它只是一个递归的教学例子。

答案 2 :(得分:1)

你是对的。你甚至不需要变量,返回地址本身已占用空间。有一些方法可以避免递归中的深度嵌套(尾递归),现代编译器在许多情况下会自动执行。但除此之外,迭代将优于空间复杂性方面的递归。

答案 3 :(得分:1)

答案是否定的,例如遍历(完整)二叉树的空间复杂度为O(log N),即树的深度。