前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >了解递归:普通函数递归和非递归栈式实现之间的区别

了解递归:普通函数递归和非递归栈式实现之间的区别

作者头像
执生
发布2020-09-27 10:49:28
8960
发布2020-09-27 10:49:28
举报
文章被收录于专栏:立权的博客立权的博客

相关链接 : 递归和栈的关系

以树的遍历为例

先序遍历:

伪代码

代码语言:javascript
复制
void preView(Node node){
    print(node.value);  // 1
    if(node.left != null){
     preView(node.left);  // 2
 }
    if(node.right != null){
     preView(node.right);  // 3
 }
}

如果我们用函数栈帧的思想,每调用一个函数,就把一个栈帧入栈

这里的问题就是:栈帧无法为我们提供足够的信息,让我们正确的继续用栈执行递归。

如果编译器编译上述的伪代码,那么在函数栈帧中会保存要返回的地址。在上述情景中,节点2的栈帧中不应该只保存节点2,应该还要保存2执行到第几行了。

继续下去是要执行第二行还是执行第三行(返回的地址)。但是软件实现一般不这么做,也不能这么做,因为我们用纯代码不用嵌入汇编的话,

很难做到像用ret这样的指令一样改变IP寄存器

可以选择在栈帧中保存一个标志,来标识要向左走(递归调用左子节点,代码中行2)还是向右(递归调用右子节点,代码中行3)走,还是说都走过了,要弹出(即已经执行了代码中行2,行3,函数执行完毕返回)。比如一个int变量,如果左子节点已入栈,但右子未入栈,就标记为1。0表示均未递归调用左右子节点,2表示都调用过。

递归子函数的栈帧弹出后,返回到针对当前节点的栈帧:有以下情况

0,如果这个int变量为0,则左右子节点都未被递归调用

1,如果这个int变量为1,则把右子节点对应栈帧入栈,并且把当前栈帧中这个int变量修改成2

2,如果这个int变量为2,则直接把当前栈帧弹出

于是当2的节点对应栈帧出栈后,5的节点对应的栈帧就有了方向,知道要把右子包成一个栈帧入栈

其实在知道左子节点入栈了,但右子节点未入栈后,没必要保存当前栈帧,因为上述伪代码对右子节点的递归是尾递归,即当前函数递归调用当前函数,但是并不期待这个递归调用

给当前的函数带来些什么,递归调用也用不到当前函数栈帧。所以可以直接弹出。

上图可简化:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-07-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档