用小例子理解递归
Understanding recursion with small example
Hi我在cpp中写了这样的小递归方法。我正在尝试理解递归
void print(int n)
{
if(n==6)
return;
print(++n);
cout<<n<<endl;
//output is 6 5 4 3 2
}
void print(int n)
{
if(n==6)
return;
print(n+1);
cout<<n<<endl;
//output is 5 4 3 2 1
}
void print(int n)
{
if(n==6)
return;
print(n++);
cout<<n<<endl;
//programme crash
}
你能解释一下内部发生了什么吗?
函数调用放置在堆栈上。把它想象成一堆盘子。任何时候在代码中调用"print(x)",它都是对这堆板的又一次添加。当函数到达其右大括号或到达返回语句时,函数将从堆栈中删除。
我认为您正在对这些函数调用print(0)。因此,print(0)是堆栈上的第一个内容。最后一个函数崩溃是因为它"永远"调用print(0),直到它没有空间容纳更多的"板"。这被称为无限递归,由于堆栈的限制,无限递归很少是无限的。
至于其他函数,"cout"语句只有在从堆栈中删除函数之后才被调用。除了print(6)调用这一个例外,这些方法中的每一个都不断在堆栈中放置新的东西。这通常被称为基本情况,是递归过程的结束。因为它开始从堆栈中级联移除板,从而允许所有cout语句发生(与无限递归的情况不同)。
要了解这些代码的区别,您必须确保了解n++、++n和n+1之间的区别。
这不是递归中的问题,而是表达式求值中的问题。我们有三个例程,当n达到6时停止;否则,它们会进行某种形式的递增和重复,并使用不同的表达式。递归调用后,打印n的本地值并返回给调用者。
请注意,每次调用print时,都会向运行时堆栈添加一个新的局部变量空间。每一个都有自己的n副本:增加一个副本不会改变其他副本。
-
++n增加n
- n+1不要更改n的本地副本,而是使用下一个更高的值再次调用该函数
- ++n使用当前值n的本地值,然后打印
有时,尝试并说明调用序列会有所帮助。
想象一个函数f1到f7的序列,其中f1调用f2,后者调用f3,依此类推。(因为我从0开始,而不是1)
f1看起来像:(和其他非常相似)
void f1(int n)
{
if(n==6)
return;
f2(++n);
std::cout<<n<<std::endl;
}
因此,由f1(0)发起的呼叫序列可以被说明为:
f1(0)--v : because f2 is called with ++n
f2(1)--v : because f3 is called with ++n etc.
f3(2)--v
f4(3)--v
f5(4)--v
f6(5)--v
f7(6)
每个函数调用都有一个返回:
// no cout of 7
return to f6(6)
cout<<... n is 6
return to f5(5)
cout<<... n is 5
return to f4(4)
cout<<... n is 4
return to f3(3)
cout<<... n is 3
return to f2(2)
cout<<... n is 2
return to f1(1)
cout<<... n is 1
现在,要使用递归,只需将编号的f替换为foo
void foo(int n)
{
if(n==6)
return;
foo(++n);
std::cout<<n<<std::endl;
}
并且调用序列(用foo(0)启动)可能看起来像
foo(0)--v -- given 0, calls foo with 1
foo(1)--v -- give 1, calls foo with 2
foo(2)--v -- etc
foo(3)--v
foo(4)--v
foo(5)--v
foo(6) - return
我称第一部分递归为
return to foo(6) (was 5)
cout<<... n is 6
return to foo(5) (was 4)
cout<<... n is 5
return to foo(4) (was 3)
cout<<... n is 4
return to foo(3) (was 2)
cout<<... n is 3
return to foo(2) (was 1)
cout<<... n is 2
return to foo(1) (was 0)
cout<<... n is 1
第二部分,所有这些函数的返回,这个特定堆栈使用的"崩溃",我有时会注意到"decursion"(旧军事术语的新用法)。(++运算符的影响使对齐具有误导性。)
因为cout在递归调用之后,(即在递归中)即使输入'n'在递归过程中增加,输出序列也在减少。
因此,这个特定的序列每行包含1位数字:
6
5
4
3
2
1
更新-为什么上次代码剪切崩溃?
最后一个代码片段崩溃,因为后增量发生在递归调用之后。因此,调用foo(0)确实调用了foo(n++),但这只是再次调用foo,然后增加n(再次)。第二个和所有后续的foo()递归都看到相同的值0,因此终止条件(n=6)永远不会发生,堆栈溢出了无限函数调用。
- 通过递归进行因子分解
- 递归函数计算序列中的平方和(并输出过程)
- 使用递归的数组的最小值.这是怎么回事
- 递归列出所有目录中的C++与Python与Ruby的性能
- 递归计数给定目录的文件和所有目录
- 如何在BST的这个简单递归实现中消除警告
- C++:正在检查LinkedList中的回文-递归方法-错误
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 递归无序映射
- TSP递归解的迭代形式
- 如何在Elixir中调用递归函数并行
- 返回递归调用和仅递归调用的区别
- 数组元素打印的递归方法
- 使用递归时获取变量的奇怪值
- 如何在C++中递归地按相反顺序打印集合
- 到连接组件算法的问题(递归)
- 如何使用递归打印修改后的星号三角形图案
- 使用递归模板动态分配的多维数组
- 递归函数有效,但无法记忆
- 包含模板文件的递归会导致编译失败