我知道递归是一种在函数本身内调用函数的技术。
但是下面的代码让我对第一次递归后cout
部分的处理方式感到困惑:
(此代码解决了河内难题塔)
#include <iostream>
using namespace std;
void move_rings(int n, int src, int dest, int other);
int main(void)
{
int rings;
cout << "Number of Rings: ";
cin >> rings;
move_rings(rings, 1, 3, 2);
system("PAUSE");
}
void move_rings(int rings, int source, int destination, int other)
{
if (rings == 1)
{
cout << "Move from " << source << " to " << destination << endl;
}
else
{
move_rings(rings - 1, source, other, destination);
cout << "Move from " << source << " to " << destination << endl;
move_rings(rings - 1, other, destination, source);
}
}
如您所见,函数move_rings
在if
语句后调用自身。
当我想象这个时,我看到一个永无止境的循环......这个函数怎么可能做到
cout << "Move from " << source << " to " << destination << endl;
部分?
程序的输出是:
Move from 1 to 3
Move from 1 to 2
Move from 3 to 2
Move from 1 to 3
Move from 2 to 1
Move from 2 to 3
Move from 1 to 3
答案 0 :(得分:5)
起初可能有点难以理解递归。当我这样想时,它“点击”了我:你有一个基本情况,这是导致递归函数而不是再调用自身的条件,然后你有另一部分(你的代码中的“else”),继续调用该函数。 “ring == 1”条件是您的基本情况。
每次使用较小的参数调用函数“move_rings”。在每次后续调用中,变量“rings”变小(因此“靠近基本情况”),直到“rings == 1”为真,然后函数停止调用自身。
希望有所帮助。
答案 1 :(得分:3)
因为每次调用move_rings()
时,它的第一个参数都会变小。最后,假设一个非无限数量的环,该函数将仅在一个环上被调用。这是导致递归返回的终止条件。
将其描绘成二叉树结构。假设一个非无限数量的节点,您最终将到达一个叶子节点,超过该节点就不再存在。然后,您可以开始遍历堆栈以及找到叶节点的其他代码路径。
答案 2 :(得分:1)
因为在每次调用move_rings
时都会传递参数rings - 1
。最后,传递的参数将为1
,rings == 1
将为真。
在处理递归(或任何类型的可重入)函数时,了解局部变量的工作方式非常重要。每次调用函数都有自己的局部变量和参数的化身。想象一下砖块(就像河内塔中的一堆)。每块砖都包含函数参数。调用函数时,该函数的参数放在堆栈顶部,并使用最顶层的砖值执行函数。当函数返回时,最顶层的砖被丢弃,返回到下面砖块的值。
答案 3 :(得分:0)
每次函数递归时,它都会得到一个递减1的响铃次数。最后,所有分支都达到rings==1
状态并终止。您可以将其想象为二叉树,其分支处于else
状态,其叶子处于if
状态。
答案 4 :(得分:0)
在else
分支中,呼叫以rings - 1
完成。由于您永远不会增加它,最终rings
将1
并且if
分支将被点击。由于此分支中不发生递归,因此该方法终止。
答案 5 :(得分:0)
每次调用move_rings函数时,响铃次数减1。最终,响铃次数为1。 但是,这段代码确实可以产生无限循环,因为它编写得不好。从不检查环数大于1.因此,如果在main函数中输入的环数小于1,则永远不会达到停止递归的条件。
答案 6 :(得分:0)
当move_rings
调用move_rings
时,函数的第二次调用将彻底恢复。它有一组完全独立的变量。就像move_rings
调用任何其他函数一样。它碰巧是调用具有相同名称并包含相同逻辑的“另一个函数”。
在函数的第二次调用中,rings
将具有较低的值,因为第一个调用传递的参数值小于其自身的值。最终,在其中一个递归调用中,该值将达到1,并且函数开头的if
测试将测试为true。这避免了进一步的递归,并且该函数返回。然后,函数的前一次调用将从它所在的位置恢复,就像它调用了任何其他函数一样。它执行打印,然后在发生类似情况时进行另一次递归调用,然后完成。等等一直“备份”。