如何计算递归函数的输出?我知道递归调用一个堆栈,但在解决一些aptitude问题时很困惑。
给出以下代码片段:
#include <stdio.h>
void fun(int a){
if(a>0){
fun(--a);
printf("%d ",a);
fun(--a);
printf("%d ",a);
}
return;
}
int main(void){
int num = 5;
fun(num);
return 0;
}
这不是任何家庭作业,但我无法在考试条件下解决这个问题。(无编译理论考试)
解决此类问题的标准方法是什么?请用一个小例子来解释。欢迎使用正确方向的指针或某个网站链接。
答案 0 :(得分:8)
拿笔和纸;绘制函数的调用以及参数 - 你将有一种二叉树。跟踪执行并在页面上写入所有相关数据。它还可以帮助您了解功能。
当您在纸上绘制时,递归调用中涉及的分支(尤其是像这样的二进制调用)非常合乎逻辑且直观。这就是我在学校里被教导的方式 - 而且这是一种理解这样的东西的好方法,至少在一开始并不是一切都那么直观。
示例:
fun [5]
/ \
fun[4] fun[3]
/ \ | \
fun[3] fun[2] fun[2] fun[1]
我画了调用树,就像你在纸上画出来一样。这应该有助于让你发现更清楚的事情。这就是我当天处理这类东西的方式,所以请相信我 - 它有效:)
答案 1 :(得分:3)
编写递归方法有点像制定一个计划来排列多米诺骨牌并将其击倒。您必须预测每个递归调用(多米诺骨牌)将如何为完成整个任务添加其部分。
这意味着递归方法的“肉”不在递归部分,但在“结束情况”中,当您不打算重新调用该方法时执行的代码部分,最后一个多米诺骨牌在链中,你推动开始的乐趣。
那么,让我们看看你的第一个例子,一个(整数)除法的递归程序。你想要实现的除法算法是“对于正d和n,令n(0)为n。继续从n(i)逐步减去d,直到在某个步骤q中,n(q)小于d你的回答是q。“
关键是首先看一下END案例。如果在开始时n已经小于d?然后你做了“零步骤”,所以你的除法结果是0。
int divide(int n, int d) {
if (n < d) {
return 0;
}
....
}
int divide(int n, int d) {
if (n < d) {
return 0;
} else {
return divide( n-d, d ) + 1;
}
}
第二次回归实际上说的是什么?它说:“我不知道如何自己计算结果,但我确实知道它比'divide(nd,d)'的结果更多。所以我将'推卸'给那个方法调用然后只需添加一个它给我回来的东西。“
这个过程还在继续。我们不断向链中添加“除”多米诺骨牌,直到我们达到分割操作,其中n“收缩”小于d ...我们的结束情况,我们的零结果。现在我们敲掉第一个多米诺骨牌(我们添加到链中的最后一个),返回“0”。多米诺骨牌开始下降。每当一个多米诺骨牌敲击另一个多米诺骨牌时,我们在方法结果中加上“1”,直到最后第一个方法调用是最后一个倒下的多米诺骨牌,并返回分割结果。
让我们尝试一些例子:
分(12,18) ---&GT;返回0,因为12小于18
divide(20, 5)
---> returns divide(20-5, 5) + 1
------> returns divide(15-5, 5) +1
---------> returns divide(10-5, 5) +1
------------> returns divide(5-5, 5) +1
---------------> returns 0, since 5-5 is 0, which is less than 5
and now the dominoes fall...
------------> returns 0 + 1
---------> returns 1 + 1
------> returns 2 + 1
---> returns 3 + 1
result is 4.
divide(8, 3)
---> returns divide(8-3, 3) + 1
------> returns divide(5-3, 3) +1
---------> returns 0, since 5-3 is 2, which is less than 3
and now the dominoes fall...
------> returns 0 + 1
---> returns 1 + 1
result is 2.
答案 2 :(得分:1)
你必须是编译器和计算机。在你走的时候写下堆栈:
输入main,用5调用fun。 有趣的是,5大于0,所以 我先减少5到4,然后再打电话给
如果你正在写作,我会转到一边并“开始一个新的堆栈”
我输入4的乐趣,大于0 我减少了4到3,然后再次打电话给你了
重复
我输入3的乐趣,大于0 我减少3比2,然后再打电话给
再次重复
我输入2的乐趣,大于0 我减少2比1,然后再次打电话给
再一次
我输入1的乐趣,大于0 我将1减1,然后再打电话给
最后一次进入,这次
我输入0的乐趣,不大于0 我回来了
现在你回到原来的位置:
我输入1的乐趣,大于0 我将1减1,然后再次打电话给我 我打印出0
在打印命令上,将其写入另一个空间,该空间现在只包含“0”。继续这个功能:
我输入1的乐趣,大于0 我将1减1,然后再次打电话给我 我打印出0 我将0减少到-1并再次打电话给
这是另一个堆栈,但-1不大于0,所以它什么都不做。我们回到功能:
我输入1的乐趣,大于0 我将1减1,然后再次打电话给我 我打印出0 我将0减少到-1并再次调用乐趣 我打印出-1
我们完成了这个堆栈。我们回到一个较旧的堆栈(我们刚刚完成了1的乐趣,所以寻找以“减1并再次调用乐趣”结束的堆栈):
我输入2的乐趣,大于0 我减少2比1,然后再次打电话给乐趣 我打印出1 我将1减1,然后再次打电话给
调用fun(0)
什么都不做,所以我们返回并继续:
我输入2的乐趣,大于0 我减少2比1,然后再次打电话给乐趣 我打印1 我将1减1,然后再次打电话给我 我打印出0
然后我们进入下一个最旧的堆栈(我们刚刚完成了2的乐趣,所以寻找以“减少到2并再次调用乐趣”结束的堆栈):
我输入3的乐趣,大于0 我减少了3到2,然后再次打电话给我 我打印出2 我减少2比1,然后再打电话给
这是一个重要的节省时间!我们之前已经调用了fun(1)
,没有必要再次通过它。 fun(1)
打印出来的是什么?查找,你会看到它为输出添加了“0-1”,所以节省时间,然后添加。
这一直持续到你完成为止。这是很多工作,但写下你当前的堆栈是完成它的最简单的方法。为了保持这个已经很久的答案简短,其余的由你决定。 :)
答案 3 :(得分:0)
在函数开头添加printf("%d\n", a);
(在if语句之外)并运行代码以查看执行流程。您可以向函数添加另一个参数,该参数将递归深度作为参数,并使用它来缩进print语句。
以下程序的输出将帮助您了解执行流程。
#include <stdio.h>
void indent(int d)
{
for(int i = 0; i < d; i++)
printf("\t");
}
void fun(int a, int d)
{
indent(d);
printf("function called with a = %d\n", a);
if(a>0)
{
fun(--a, d + 1);
indent(d);
printf("%d\n", a);
fun(--a, d + 1);
indent(d);
printf("%d\n", a);
}
else
{
indent(d);
printf("returning as a<%d> <= 0\n", a);
}
return;
}
int main(void)
{
int num = 5;
fun(num, 0);
return 0;
}
这是输出:
function called with a = 5
function called with a = 4
function called with a = 3
function called with a = 2
function called with a = 1
function called with a = 0
returning as a<0> <= 0
0
function called with a = -1
returning as a<-1> <= 0
-1
1
function called with a = 0
returning as a<0> <= 0
0
2
function called with a = 1
function called with a = 0
returning as a<0> <= 0
0
function called with a = -1
returning as a<-1> <= 0
-1
1
3
function called with a = 2
function called with a = 1
function called with a = 0
returning as a<0> <= 0
0
function called with a = -1
returning as a<-1> <= 0
-1
1
function called with a = 0
returning as a<0> <= 0
0
2
4
function called with a = 3
function called with a = 2
function called with a = 1
function called with a = 0
returning as a<0> <= 0
0
function called with a = -1
returning as a<-1> <= 0
-1
1
function called with a = 0
returning as a<0> <= 0
0
2
function called with a = 1
function called with a = 0
returning as a<0> <= 0
0
function called with a = -1
returning as a<-1> <= 0
-1
1
3
跟踪递归真的很有趣。享受!
答案 4 :(得分:-1)
这是一个难以回答的问题,只有笔和纸,我认为这是为了确保你已经理解了计算机是如何做到的,特别是对fun
的两次递归调用。
在纸上模拟执行时,请记住,在递归调用fun
时,处理器会记住局部变量的内容(此处参数为a
)和哪个语句为在被叫函数结束后返回。