我目前正在使用PHP,因此这个示例将使用PHP,但该问题适用于多种语言。
我正在与我的一个恶魔一起从事这个项目,而且一如既往我们遇到了一个大问题。现在我们都回家了,无法解决问题。那天晚上我们都找到了解决方案,只是我使用了一个循环来解决这个问题,并且他使用了递归。
现在我想告诉他循环和递归之间的区别,但我无法想出一个需要通过正常循环递归的解决方案。
我将制作两者的简化版本,我希望有人可以解释一个人与另一个人的不同之处。
请原谅我有任何编码错误
循环:
printnumbers(1,10);
public function printnumbers($start,$stop)
{
for($i=$start;$i<=$stop;$i++)
{
echo $i;
}
}
现在上面的代码只是打印出数字。
现在让我们通过递归来做到这一点:
printnumbers(1,10);
public function printnumbers($start,$stop)
{
$i = $start;
if($i <= $stop)
{
echo $i;
printnumbers($start+1,$stop);
}
}
上面的方法与循环完全相同,但只有递归。
任何人都可以向我解释使用这些方法之一有什么不同。
答案 0 :(得分:11)
循环和递归在很多方面都是等价的。没有程序需要一个或另一个,原则上你总是可以从循环转换为递归,反之亦然。
递归更强大,因为将递归转换为循环可能需要一个必须自己操作的堆栈。 (尝试使用循环遍历二叉树,你会感到痛苦。)
另一方面,许多语言(和实现)(例如Java)不能正确地实现尾递归。尾递归是指你在函数中做的最后一件事就是自己调用(就像在你的例子中一样)。这种递归不必消耗任何堆栈,但在许多语言中它们都会消耗,这意味着你不能总是使用递归。
答案 1 :(得分:8)
通常,使用递归更容易表达问题。当你谈论树状数据结构(例如目录,决策树......)时尤其如此。
这些数据结构本质上是有限的,所以处理它们的大部分时间都是递归的更清晰。
当堆栈深度经常受到限制,并且每个函数调用都需要一个堆栈时,并且在谈论可能无限的数据结构时,您将不得不放弃递归并将其转换为迭代。
特别是函数式语言擅长处理“无限”递归。命令式语言专注于类似迭代的循环。
答案 2 :(得分:2)
通常,递归函数会占用更多的堆栈空间(因为它实际上是一大堆函数调用),而迭代解决方案却不会。这也意味着迭代解决方案通常会更快,因为。
我不确定这是否适用于像PHP这样的解释语言,但解释器可以更好地处理它。
答案 3 :(得分:2)
循环会更快,因为在执行额外的函数调用时总会有开销。
学习递归的一个问题是很多给出的例子(例如,阶乘)都是使用递归的坏例子。
如果可能的话,坚持循环,除非你需要做一些不同的事情。使用递归的一个很好的例子是使用多级子节点循环遍历Tree中的每个节点。
答案 4 :(得分:2)
递归有点慢(因为函数调用比设置变量慢),并且在大多数语言的调用堆栈上使用更多空间。如果您尝试printnumbers(1, 1000000000)
,递归版本可能会抛出PHP致命错误甚至500错误。
在某些情况下,递归是有意义的,比如对树的每个部分做一些事情(获取目录及其子目录中的所有文件,或者可能搞乱XML文档),但它有其代价 - 速度,堆栈占用空间,以及确保它不会卡住自己的时间,直到它崩溃为止。如果循环更有意义,那肯定是要走的路。
答案 5 :(得分:1)
好吧,我不了解PHP,但大多数语言都为每次递归生成一个函数调用(在机器级别)。所以他们有可能使用大量的堆栈空间,除非编译器产生尾调用优化(如果你的代码允许)。 在这种意义上,循环更“高效”,因为它们不会增加堆栈。递归的优点是能够更自然地表达某些任务。
在这种特定情况下,从概念(而不是实施)的角度来看,这两种解决方案是完全等效的。
答案 6 :(得分:0)
与循环相比,函数调用有自己的开销,比如分配堆栈等。在大多数情况下,循环比它们的递归对应物更容易理解。
此外,如果启动和停止之间的差异很大并且同时运行此代码的实例太多(这可能会在您获得更多流量时发生),您将最终使用更多内存,甚至可能耗尽堆栈空间
答案 7 :(得分:0)
对于像这样的扁平结构,你真的不需要递归。第一个代码我曾经使用递归来管理物理容器。每个容器可能包含东西(它们的列表,每个都有权重)和/或更多容器,它们具有权重。我需要一个容器的总重量和所有它。 (我用它来预测装满野营装备的大背包的重量,没有包装和称重它们。)这很容易用递归进行,并且环路会更加困难。但是,自然适合一种方法的许多问题也可以用另一种解决。
答案 8 :(得分:0)
堆栈溢出。
不,我不是指网站或其他东西。我的意思是“堆栈溢出”。