假设我有一个以递归方式访问的结构。
伪代码:
visit(node n)
{
if (n == visited)
return;
//do something
setVisited(n);
foreach child_node in n.getChildren(){
visit(child_node);
}
}
根据这个thread尾递归可能发生在:
尾递归基本上是在:
- 只有一个递归调用
- 该调用是函数
中的最后一个语句
在上面的伪代码中,递归调用是最后一个语句,但由于调用发生在循环内,因此存在多个递归调用。 我想编译器无法检测到尾递归。
我的问题是:
无论如何重构上面的代码,以使其尾递归? 我正在寻找一种不会删除递归的解决方案,如果有的话(例如,我不想使用堆栈来模拟递归并将其转换为迭代函数)。
这可能吗?
如果语言相关,我正在使用C ++。
答案 0 :(得分:2)
尾递归是模仿没有堆栈[或任何其他数据结构]的循环,即O(1)
额外空间。
当前的问题AFAIK,树/图遍历[假设每个节点中没有parent
字段]无法在如此复杂的情况下完成[O(n)
时间,O(1)
空间]因此不能用单个循环完成,没有堆栈。因此,不可能进行尾递归重构。
编辑:问题可以在O(1)空间中解决,但是O(n ^ 2)时间[这是双循环],如this post中所示。< / p>
答案 1 :(得分:2)
所以,实际上你可以总是重构一个函数,使其成为尾递归...大多数用其他语言编程的人都会使用continuation来高效编码。但是,我们更具体地看一下C / C ++语言,所以我假设只是通过编写函数本身来解决这个问题(我的意思是没有在语言中添加通用的延续框架)。
让我们假设您的函数的迭代版本应该如下所示:
void iterative() {
while (cond)
dosomething()
}
然后,将其转换为尾递归函数只需编写:
void tailrecursive() {
if (!cond)
return;
dosomething();
tailrecursive();
}
大多数情况下,您需要通过&#39; 州&#39;递归调用添加了一些以前没用的额外参数。在您的特定情况下,您有一个预订树遍历:
void recursive_preorder(node n) {
if (n == visited)
return;
dosomething(n);
foreach child_node in n.getChildren() {
recursive_preorder(child_node);
}
}
迭代等价物需要引入一个堆栈来记住资源管理器节点(因此,我们添加了推/弹操作):
void iterative_preorder(node n) {
if (n == visited)
return;
stack worklist = stack().push(n);
while (!worklist.isEmpty()) {
node n = worklist.pop()
dosomething (n);
foreach child_node in n.getChildren() {
worklist.push(child_node);
}
}
}
现在,采用preorder树遍历的这个迭代版本并将其转换为尾递归函数应该给出:
void tail_recursive_preorder_rec(stack worklist) {
if (!worklist.isEmpty()) {
node n = worklist.pop()
dosomething (n);
foreach child_node in n.getChildren() {
worklist.push(child_node);
}
}
tail_recursive_preorder_rec(worklist)
}
void tail_recursive_preorder (node n) {
stack worklist = stack().push(n);
tail_recursive_preorder_rec(worklist);
}
并且,它为您提供了一个尾递归函数,它将由编译器很好地优化。享受!
答案 2 :(得分:1)
我认为你不能。尾递归基本上是快捷方式的各种功能调用程序,以避免在调用返回调用函数后也立即返回的不必要的代价。但是,在这种情况下,在任何给定的递归级别,您(可能)进行多次调用,因此您需要能够从这些调用返回,然后再创建另一个,这不是尾递归方案。你最希望的是最后一个被优化为尾调用,但是我怀疑编译器是否可以检测到它,因为正如你所说的那样,它在循环中并且你在迭代的数据在编译时是未知的。 / p>
我想不出一种方法可以改变算法以使其具有尾递归性 - 你总是会有多个孩子的潜力,这会让它变得混乱。