我遇到了this视频,该视频讨论了如何使用for循环编写大多数递归函数,但是当我想到它时,我看不出两者之间的逻辑差异。我在这里找到了this主题,但它只关注实际差异,网络上的许多其他类似主题也是如此,那么循环和递归的处理方式有什么逻辑差异?
答案 0 :(得分:1)
前面的底线 - 递归更加通用,但实际上通常效率低于循环。
如果您希望这样做,原则上循环可以始终实现为递归。实际上,堆栈资源的限制严重限制了您可以解决的问题的大小。我可以并且已经构建了迭代十亿次的循环,除非我确定编译器可以并且将递归转换为循环,否则我永远不会尝试递归。由于堆栈限制和效率,人们经常试图找到递归的循环等价物。
尾递归总是可以转换为循环。但是,有些递归无法转换。作为一个例子,我使用实验的统计设计。有时大型设计是由#34; cross"几个较小的子设计。交叉是将第二个设计的每一行连接到第一个设计的每一行的位置。对于两个子设计,所有这些需求都是简单的嵌套循环,但对于三个或更多设计,您需要提高嵌套级别,为每个额外的子设计添加一个嵌套级别。因此,虽然原则上这是嵌套循环,但实际上嵌套的数量是可变的。如果您尝试使用循环实现它,则每次处理要交叉的不同数量的子设计时,您都必须修改程序以添加/减去嵌套循环,因此您无法编写不可变的基于循环的版本。这可以通过递归轻松实现。在这种情况下,我很乐意交换一点点效率,因为我在6年前编写并调试了代码并且没有必须修改它,因为尽管创建了许多复杂的交叉设计,然后
答案 1 :(得分:0)
考虑这一点的一种方法是递归或迭代的选择取决于您如何看待要解决的问题。某些"思维方式"更自然地引导递归解决方案,其他思维方式导致更多迭代解决方案。对于任何问题,您原则上可以通过一种方式来思考,为您提供递归解决方案或为您提供迭代解决方案的方法。 (有时迭代解决方案最终会模拟递归堆栈,但那里没有实际的递归。)
这是一个例子。你有一个整数数组(正数或负数),你想找到最大的段总和。段是连续的数组的一部分。所以在数组[3,-4,2,1,-2,4]中,最大段总和为5,你可以从段[2,1,-2,4]获得;它的总和是5。
好的 - 那我们怎么解决这个问题呢?你可能做的一件事是这样的理由:"如果我知道左半部分中的最大段总和,以及右半部分中的最大段总和,那么也许我可能会以某种方式将这些段塞在一起并找出最大段总计"。这个想法要求你在两个子区域找到最大的段总和,这是原始问题的一个较小的实例。这是递归,因此将这个想法直接转换为代码将是递归的。
但最大段总和问题不是递归"或者"迭代" - 它可以是两者,取决于您对解决方案的看法。我在上面给出了递归思考过程。这是一个迭代过程:"好吧,如果我将每个段中的元素相加,从某个索引i开始并以某个索引j结束,我可以采取最大值来解决问题&#34 ;。并且直接尝试对这种方法进行编码会给你三次嵌套循环(并且在分配上有一个坏标记,因为它非常低效!)。
因此,同样的问题,取决于问题的概念化,可以导致递归或迭代解决方案。现在,我碰巧选择了一个问题,有很多方法可以解决它,并且有合理的递归和迭代解决方案。然而,一些问题只允许一种类型的解决方案,并且该解决方案可以使用递归或迭代最自然地实现。例如,如果我要求您编写一个不断要求用户输入字母直到输入y或n的函数,您可能会开始思考:"不断重复提示并要求输入...&#34 ;在你知道之前你有一些迭代代码。也许你可能会反复思考:"如果用户输入y或n,我就完成了;否则询问用户y或n" ...在这种情况下,您将生成递归算法。但是这里的递归并没有给你太多:它不必要地使用堆栈并且不会使程序更快。 (递归有时会更容易证明正确性,在这种情况下,即使你可以交替给出一个合理的迭代解决方案,你也可以递归地呈现一些东西。)