递归概念的澄清
clarification on recursion concept
如果我写下面所示的代码,我对递归有疑问
inorder(p){
if(p!=NULL){
inorder(p->link); //step 1
cout<<p->info<<" "; //step 2
inorder(p->link); //step 3
}
}
这里,我的怀疑是,当执行步骤1时,控制返回到函数,然后再次执行步骤1,控制将返回到函数直到p为NULL,如果这是过程,那么控制将如何进入步骤2,即"cout"和步骤3。。。
我无法循环我大脑中的代码。。。
在步骤1中的"控制返回到同一功能"之前,CPU做了一个重要的步骤:它在inorder
的代码中标记了一个位置,一旦inorder
的"第二级"返回,它就需要重新开始执行(即步骤1之后的位置)。在那里,"第二级"在进入"第三级"、"第四级"等之前再次标记返回位置。最终,N
级获得NULL
,因此它立即返回。然后N-1
级开始打印info
,并继续第二次调用inorder
。现在返回位置不同了——它就在步骤3之后。一旦N
级结束,N-1
级也结束,返回N-2
级,然后返回N-3
级,依此类推,直到第一级退出。
下面是一个示例树:
A
/
B C
/
D E
有序遍历的过程如下:
inorder(A) -- step 1, level 1
inorder(B) -- step 1, level 2
inorder(NULL) -- returns
cout << B -- step 2, level 2
inorder(NULL) -- returns
return -- level 2 returns
cout << A -- step 2, level 1
inorder(C) -- step 3, level 2
inorder(D) -- step 1, level 3
inorder(NULL) -- returns
cout << D -- step 2, level 3
inorder(NULL) -- returns
return -- level 3 returns
cout << C -- step 2, level 2
inorder(E) -- step 1, level 3
inorder(NULL) -- returns
cout << E -- step 2, level 3
inorder(NULL) -- returns
return -- level 3 returns
return -- level 2 returns
return -- level 1 returns
如果您想了解递归是如何工作的,您应该了解call stack
。这是链接。希望这会有所帮助。。。
假设p
指向A
,p->link
(也就是A.link
)被称为q并指向B
,而q->link
(也就是说B.link
)为NULL,则称其为r。
p q r
----->A----->B----->0
现在我们称之为inorder(p)
。
- p不为NULL,因此interder(p)调用interder(q)(步骤1)
- q不为NULL,因此interder(q)调用interder(r)(步骤1)
- r为NULL,因此interder(r)跳过块并将控制权返回给interder(q)
- interder(q)打印B.info(步骤2)
- interder(q)调用interder(r)(步骤3),后者再次将控制返回给interder(q),后者将控制返回到interder(p)
- interder(p)打印A.info(步骤2)
- interder(p)调用interder(q)(步骤3),它再次运行2-5(再次打印B.info),然后interder(p)将控制权返回给调用它的对象。
我第一次遇到递归是用阶乘函数。阶乘是数学中的一个基本函数,给定一个数N,它返回一个等于所有小于N的正整数的乘积的数。
考虑一下C++中的这段简单代码,它很好地演示了递归。
int fact(int x)
{
if(x==1)
return 1;
x = x*fact(x-1);
return x;
}
它输入一个整数,然后在递减整数后调用自己,直到整数为1,并返回x。注意行"return x;"直到该行结束之前才调用。
当对interder(p->link)进行第一次调用时,将其视为检查点。它将一直调用到达到NULL。然后执行第2行,第二次调用interder(p->link)也是如此。这就形成了一个树
interder(p->link)->coutinfo->interder(p->link)/^/^V/V/订单()->cout->interder()订单()->cout->订单().^/\.^/\.|…|。.||interder(p->link)//(p->link为NULL)interder(p->link)//[(p->link为NULL)
我认为您的代码是正确的。您正试图以以下方式打印出一个链接列表:
inorder(p){
if(p!=NULL)
{
inorder(p->link); //step 1 to navigate to next link
cout<<p->info<<" "; //step 2 as main operation
inorder(p->link); //step 3 to navigate to next link
}
}
翻译成英文
inorder(p)
{
if p points to a linked list
{
print the list p points to;
print content of p;
print the list p points to again;
}
if p points to a null linked list
{
do nothing;
}
}
然后,1->2->3的链表将输出((3)2(3))1(((3
正如您所看到的,只有在步骤1遇到空链表时,控制才会传递到步骤2。打印出信息后,将其传递到步骤3。
因此,
当链接列表位于节点"3"时,
步骤1遇到null并返回;
步骤2打印出3;
步骤3遇到null并返回;
当节点"2"的链表
步骤1执行它在节点"3"上所做的任何操作;//打印3
步骤2打印出2;
步骤3执行它在节点"3"上所做的任何操作;//打印3
当节点"1"的链表
步骤1执行它在节点"2"上所做的任何操作;//打印3 2 3
步骤2打印出1;
步骤3执行它在节点"2"上所做的任何操作;//打印3 2 3
考虑一个游戏,如果你在家里的不同地方留下了5条消息。每条信息都会引导您进入下一条。要赢得游戏,您必须找到所有5条消息并将它们返回给游戏主机。但是,您无法在找到消息时拾取这些消息。。。你必须记住它们的位置,并按照你找到它们的相反顺序把它们捡起来。
你会走到第一个项目,记下它在哪里,然后顺着线索走;记下你以后需要返回的地方,以便找到线索。接下来的4条线索你也可以这样做。
当你找到最后一条线索,所以不再存在时,你会开始向后看,回到你发现每条线索的地方,检索它们。
找到第一条线索就像你第一次调用"有序()"。从线索到第二条线索就像你对"有序者(p->link)"的呼唤。在找到所有线索后提取线索就像返回代码中的"步骤2"一样。
每次调用函数顺序时,数据(称为激活帧或帧)都会放在称为调用堆栈的数据结构上。每个帧都跟踪函数本地的数据,例如传递到函数中的参数、指向函数调用方激活帧的指针,以及重要的是,要在调用函数中执行的下一行代码的地址。因此,当你递归地调用interder时,你正在递归地将帧添加到这个堆栈中,每个帧都包含下一行代码的地址,当相应的函数完成并将控制权返回给函数调用方时,该代码应该被执行。
换言之,当您从称为步骤1的行调用interder,并且传入的参数p为NULL时,当函数退出时,调用函数将在称为步骤2的行开始执行。
查看维基百科页面http://en.wikipedia.org/wiki/Call_stack了解更多信息。
理解与调用堆栈相关的概念将有助于理解递归的情况。
- 通过递归进行因子分解
- 递归函数计算序列中的平方和(并输出过程)
- 使用递归的数组的最小值.这是怎么回事
- 递归列出所有目录中的C++与Python与Ruby的性能
- 递归计数给定目录的文件和所有目录
- 如何在BST的这个简单递归实现中消除警告
- C++:正在检查LinkedList中的回文-递归方法-错误
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 递归无序映射
- TSP递归解的迭代形式
- 如何在Elixir中调用递归函数并行
- 返回递归调用和仅递归调用的区别
- 数组元素打印的递归方法
- 使用递归时获取变量的奇怪值
- 如何在C++中递归地按相反顺序打印集合
- 到连接组件算法的问题(递归)
- 如何使用递归打印修改后的星号三角形图案
- 使用递归模板动态分配的多维数组
- 递归函数有效,但无法记忆
- 包含模板文件的递归会导致编译失败