在任何使用递归的地方都可以使用for
循环吗?如果递归通常比for
循环慢,那么为什么要使用它而不是for
循环迭代呢?
如果总是可以将递归转换为for
循环,那么有没有一种经验法则来做到这一点呢?
递归通常较慢,因为所有函数调用都必须存储在堆栈中,以允许返回到调用方函数。 在许多情况下,需要分配内存并复制以实现范围隔离。
一些优化,如尾调用优化,使递归更快,但并不总是可能的,并且并非所有语言都实现了这些优化。
使用递归的主要原因是
当然,每个递归可以被建模为某种循环:这就是CPU最终要做的。 而递归本身,更直接地说,意味着将函数调用和作用域放入堆栈中。 但将您的递归算法更改为循环算法可能需要大量工作,并使您的代码难以维护:对于每个优化,应仅在某些 profiling 或证据显示其必要性时尝试。
每次使用递归时,都可以用for循环吗?
是的,因为在大多数CPU中,递归是通过循环和堆栈数据结构进行建模的。
如果递归通常比较慢,那么使用它的技术原因是什么?
并不是“通常比较慢”,而是应用不正确的递归才会比较慢。此外,现代编译器善于将一些递归转换为循环,甚至不需要提前请求。
如果总是能够将递归转换为for循环,那么是否有经验法则来实现这一点?
对于最好迭代解释的算法,请编写迭代程序;对于最好递归解释的算法,请编写递归程序。
例如,在许多编程语言中搜索二叉树、运行快速排序和解析表达式通常是通过递归解释的。这些最好也以递归方式编码。另一方面,计算阶乘和斐波那契数列在迭代方面更容易解释。对于它们使用递归就像用大锤打苍蝇:即使大锤做得很好,这也不是一个好主意+。
//Triangular.java
import java.util.*;
class Triangular {
public static int iterativeTriangular(int n) {
int sum = 0;
for (int i = 1; i <= n; i ++)
sum += i;
return sum;
}
public static void main(String args[]) {
Scanner stdin = new Scanner(System.in);
System.out.print("Please enter a number: ");
int n = stdin.nextInt();
System.out.println("The " + n + "-th triangular number is: " +
iterativeTriangular(n));
}
}//enter code here
使用递归算法:
//Triangular.java
import java.util.*;
class Triangular {
public static int recursiveTriangular(int n) {
if (n == 1)
return 1;
return recursiveTriangular(n-1) + n;
}
public static void main(String args[]) {
Scanner stdin = new Scanner(System.in);
System.out.print("Please enter a number: ");
int n = stdin.nextInt();
System.out.println("The " + n + "-th triangular number is: " +
recursiveTriangular(n));
}
}
正如Thanakron Tandavas所said,递归在解决可以通过分治技术解决的问题时很有用。
例如:汉诺塔
条件如下:
我记得我的计算机科学教授曾经说过,所有具有递归解决方案的问题也都有迭代解决方案。他说递归解决方案通常较慢,但在易于推理和编码时,它们经常被使用。
然而,在更高级的递归解决方案中,我不认为总是能够使用简单的for
循环来实现。
大多数答案似乎假定 iterative
= for loop
。如果您的for loop未受限制 (a la C,可以对循环计数器进行任何操作),那么这是正确的。如果它是一个真正的 for
循环(例如Python或大多数函数语言中无法手动修改循环计数器),那么就不正确。
所有(可计算)函数都可以递归地实现,并使用 while
循环 (或条件跳转,基本上是相同的东西)。如果你真的仅限于使用 for loops
,那么你只会得到其中的一个子集(原始递归函数,如果你的基本运算是合理的)。毫无疑问,这是一个相当大的子集,包含了你在实践中可能遇到的每一个函数。
更重要的是,许多函数在递归实现时非常容易实现,在迭代实现时则非常困难(手动管理调用堆栈是无效的)。
使用递归加上记忆化可能会比纯迭代方法更有效率,例如查看此链接: http://jsperf.com/fibonacci-memoized-vs-iterative-for-large-n
简短回答:在大多数情况下,循环比递归占用更少的内存,但递归更快。然而,通常有方法可以改变循环或递归以使其运行更快。