我目前正在编写一些分治算法,其中函数递归在任何地方使用,但我有一个非常模糊的想法或不知道它究竟是如何工作的,这就是为什么我在这里发布它并希望你不介意它也是基本
例如,如果我们有以下代码:
#include<iostream>
using namespace std;
void Recursion(int n)
{
cout << n << endl;
if(n > 0)
{
Recursion(n-1);
}
cout<<n<<endl;
}
int main()
{
Recursion(3);
return 0;
}
我测试了Recursion(3),终端中的打印输出是:
3
2
1
0
0
1
2
3
我可以理解函数的递归调用的概念,但我不理解它是如何工作的机制。例如,他们无法再次调用该函数后会做什么?例如,在这里,我可以理解它打印从3到0,但为什么它也会再次从0打印到3?我听说是因为函数递归存储在一个递归的堆栈中,当它到达“底部”时,它也必须删除。
但无论如何,我不知道。那么,任何人都可以帮助我,并清楚地告诉我这里发生了什么以及函数调用的确切流程吗?
感谢您的帮助!
答案 0 :(得分:5)
理解递归的关键是调用堆栈的概念。调用堆栈由“框架”组成。堆栈帧包含函数的局部变量和不可见的返回地址。经典的物理比喻是一堆板块。当您进行函数调用时,将板(堆栈框架)添加到堆栈顶部。从功能返回时,顶板(堆叠框架)被移除。您只能使用顶部的板(堆叠框架)。
递归函数的工作方式与普通函数相同。它们有点棘手,因为您可以在给定时间在堆栈上拥有多个局部变量实例。但是,与其他函数一样,该函数仅指堆栈顶部的堆栈帧。
为了说明这是如何工作的,让我们逐步介绍一下程序,显示调用堆栈如何增长和缩小。
让我们从基本案例开始:0。Recursion(0);
Recursion(0);
输入堆栈增长的递归:堆栈底部 - &gt; | 0 |&lt; -Top of stack cout << n << endl;
n的值为0,因此输出为“0”if (n > 0)
。 0不大于0因此不会调用Recursion(-1)。cout << n << endl;
n的值为0,因此输出为“0”输出为
0
0
很简单,没有发生递归。让我们迈出下一步。 Recursion(1);
Recursion(1);
输入递归:堆栈底部 - &gt; | 1 |&lt; -Top of stack cout << n << endl;
n的值为1,因此输出为“1”if (n > 0)
。 1大于0,因此调用Recursion(0);
。cout << n << endl;
此堆栈帧中的n值为0,因此输出为“0”if (n > 0)
。 0不大于0,因此该函数不会递归。cout << n << endl;
n的值为0,因此输出为“0”cout << n << endl;
n的值为1,因此输出为“1”输出
1
0
0
1
让我们最后一次执行n == 2
Recursion(2);
输入递归:底部 - &gt; | 2 |&lt; -Top cout << n << endl;
“2”if (n > 0)
。 2大于0,因此调用Recursion(1);
。cout << n << endl;
“1”if (n > 0)
。 1大于0,因此调用Recursion(0);
。cout << n << endl;
“0”if (n > 0)
。 0不大于0因此该函数不会再次递归。cout << n << endl;
“0”cout << n << endl;
“1”cout << n << endl;
“2”输出
2
1
0
0
1
2
答案 1 :(得分:4)
你是对的,我也发现递归函数难以理解。这就是我所做的,如果我看到一个递归函数:在你的脑海里一步一步地运行所有代码。这个建议可能看似微不足道,但大部分时间它对我有用。我们来看看你的代码: 你用参数3调用Recursion()函数。它打印n和n> 0,这就是它调用Recursion(2)的原因(注意我们没有从Recursion(3)调用返回我们仍在其中,现在我们也是在递归(2)。同样是递归(1)和0.现在n&gt; 0条件是假。它打印0.我们从递归返回(0)我们打印1并从递归(1)返回它去关于递归(3)
Recursion(3)
Recursion(2)
Recursion(1)
Recursion(0)
return from Recursion(0)
return from Recursion(1)
return from Recursion(2)
return from Recursion(3)
答案 2 :(得分:3)
调用自身的函数与调用另一个函数的函数没有区别:在继续之前,它必须等待它调用的函数返回。
顺便说一句,递归可能看起来很优雅,但一般来说它并不是最有效的编程方式:它使内联函数不可能实现,因此保证了上下文切换的开销。总是有一种更有效的方法来获得递归函数的相同结果。但是对于某些问题,递归实现更直观,并且在任何重要方面都不会更慢。您在评论中给出的示例,合并排序,是一个很好的例子。
关于此的几个有趣的讨论:
Way to go from recursion to iteration
Can every recursion be converted into iteration?
我的最后建议:当问题不需要这种方法时,不要去递归,例如计算阶乘时。
答案 3 :(得分:2)
有时通过从基本情况开始,即不会递归的情况,更容易理解递归。 在您的示例中,当n <= 0时,无需更多呼叫即可解析呼叫。让我们从那开始。
如果调用Recursion(0),则预期结果是两次打印零,一次在if之前,一次在if之后。 if中的代码不会执行。
现在,Recursion(1)首先打印第一个值,然后调用Recursion(0),然后像之前一样打印0,然后执行返回到Recursion(1),再次打印1,执行递归(0)。这就是为什么你看到1 0 0 1。 这同样适用于递归(2),它将递归(1)的结果包装在两个左右。