递归函数的非递归化
2015-08-02 21:27
204 查看
昨天做usaco的一道题,把递归函数非递归化看看对效率的提升,对如何转化,做了一些尝试。
![](http://img.blog.csdn.net/20150802201722520)
我们手动构造一个堆栈,在一次函数过程中模拟递归。
首先,二叉树的先序遍历 递归写法:
函数局部变量,只有root,需要保存在自己维护的堆栈,而执行到的位置,此处并不需要保存,因为我们知道函数执行left后,会以right为root进入新的栈帧,那么我们提前把root->right push进自己维护的堆栈就好了.
while循环每一次,就代表执行一次函数
我把非递归的版本放进leetcode 二叉树先序遍历那一题进行了测试,结果如下
![](http://img.blog.csdn.net/20150802204729269)
这个gen函数是生成长度为level的素数,而且从右往左每减少一位得到的数仍然是素数,比如23是素数,减少一位,2仍然是素数
函数的局部变量,不仅有函数参数level,还有i,i代表了执行到何处,可以像之前那样,把5个新状态反序push进栈里面就好了呢?
可以发现,num这个全局变量受到了局部变量i的影响,而在进入新的栈帧时,i的信息没有用到,所以不能简单一次push 5个状态,可以做如下处理:
增加visit变量代表是进入该函数还是返回该函数
而实际上,这种visit方式不好,因为需要额外的开销,有时候局部变量不是整数,这样处理就不行了,而此题我们可以修改递归函数,使得i的信息可以被利用,如下
对应的非递归版如下:
理解递归过程
递归是函数调用自己本身,每调用一次自己,就进入一个新的堆栈帧非递归化
通过观察递归的过程,我们知道这是通过堆栈来实现的,那么非递归化,就是我们手动构造一个堆栈,在一次函数过程中模拟递归。
既然我们构造堆栈,堆栈要保存什么信息?
保存函数的局部变量,及执行到的语句。
堆栈的维护
每调用一次函数,相对应就要push一个状态,离开函数,就要pop
示例
简单起见,我们首先来看看二叉树遍历的非递归化首先,二叉树的先序遍历 递归写法:
void preOrder(TreeNode* root) { if(root==NULL) return; printf("%d ",root->val); preOrder(root->left); preOrder(root->right); }
函数局部变量,只有root,需要保存在自己维护的堆栈,而执行到的位置,此处并不需要保存,因为我们知道函数执行left后,会以right为root进入新的栈帧,那么我们提前把root->right push进自己维护的堆栈就好了.
while循环每一次,就代表执行一次函数
void preOrder(TreeNode* root) { stack<TreeNode*> q; TreeNode* tp; q.push(root); while(!q.empty()) { tp = q.top(); q.pop(); if(tp==NULL) continue; q.push(tp->right); q.push(tp->left); } }
我把非递归的版本放进leetcode 二叉树先序遍历那一题进行了测试,结果如下
更复杂的递归函数
二叉树的遍历是比较简单的递归函数,如果函数复杂一点,拿我之前尝试的例子做个说明,函数的递归写法如下:这个gen函数是生成长度为level的素数,而且从右往左每减少一位得到的数仍然是素数,比如23是素数,减少一位,2仍然是素数
void gen(int level) { int sum = 0; if(0 == level) { num = '\0'; sum = atoi(num); fou<<sum<<endl; return; } for(int i=0; i<5; ++i) { num[n-level] = sdigit[i]; num[n-level+1] = '\0'; sum = atoi(num); if(isPrime(sum)) gen(level-1); } }
函数的局部变量,不仅有函数参数level,还有i,i代表了执行到何处,可以像之前那样,把5个新状态反序push进栈里面就好了呢?
可以发现,num这个全局变量受到了局部变量i的影响,而在进入新的栈帧时,i的信息没有用到,所以不能简单一次push 5个状态,可以做如下处理:
增加visit变量代表是进入该函数还是返回该函数
typedef struct state { state(int a,int b){l=a,i=b;} int l; int i; }state; //state 中l代表局部变量lev,i代表for循环变量i void noRecursive(int lev) { int sum = 0; frame.push(state(lev,0)); num[n-lev] = sdigit[0]; while(!frame.empty()) { state tp = frame.top(); if(tp.l == 0) { num = '\0'; sum = atoi(num); fou<<sum<<endl; frame.pop(); visit[tp.l][tp.i] = 0; continue; } if(!visit[tp.l][tp.i]){ visit[tp.l][tp.i] = 1; num[n-tp.l] = sdigit[tp.i]; num[n-tp.l+1] ='\0'; sum = atoi(num); if(isPrime(sum)){ frame.push(state(tp.l-1,0)); } } else{ frame.pop(); visit[tp.l][tp.i] = 0; if(tp.i<4) { frame.push(state(tp.l,tp.i+1)); } } } }
而实际上,这种visit方式不好,因为需要额外的开销,有时候局部变量不是整数,这样处理就不行了,而此题我们可以修改递归函数,使得i的信息可以被利用,如下
void gen2(int level, int j) { int sum = 0; if(0 == level) { num[n-1] = sdigit[j]; num = '\0'; sum = atoi(num); if(isPrime(sum)) fou<<sum<<endl; return; } if(level == n-1) num[n-level-1] = cdigit[j]; else num[n-level-1] = sdigit[j]; num[n-level] = '\0'; sum = atoi(num); if(isPrime(sum)) { for(int i=0; i<5; ++i) { gen2(level-1,i); } } }
对应的非递归版如下:
void noRecursive2(int lev,int j) { int sum = 0; frame.push(state(lev,j)); num[n-lev-1] = sdigit[j]; while(!frame.empty()) { state tp = frame.top(); if(tp.l == 0) { num[n-1] = sdigit[tp.i]; num = '\0'; sum = atoi(num); if(isPrime(sum)) fou<<sum<<endl; frame.pop(); continue; } if(tp.l == n-1) num[n-tp.l-1] = cdigit[tp.i]; else num[n-tp.l-1] = sdigit[tp.i]; num[n-tp.l] ='\0'; sum = atoi(num); frame.pop(); if(isPrime(sum)){ for(int i=4; i>=0; --i) frame.push(state(tp.l-1,i)); } } }
相关文章推荐
- angularJS--多个控制器之间的数据共享
- java中文件的绝对路径,相对路径和抽象路径
- 异常类
- Binary Tree Maximum Path Sum
- (转)最小生成树
- angularJS--多个控制器之间的数据共享
- Android OpenGL ES2.0之光照的学习总结
- MAP基本操作函数
- 请高手指点,PHP问题,解密出一个文件,不知道这个是什么用的,怎么使用,谢谢好友指点
- 《MFC游戏开发》笔记八 游戏特效的实现(二):粒子系统
- 《MFC游戏开发》笔记九 游戏中的碰撞判定初步&怪物运动简单AI
- 面试准备---排序算法--冒泡排序
- Eclipse快捷键大全(转载)
- iOS:KVO的概述与使用
- 程序员最应去的网站有哪些?
- 浅析PageRank算法
- hdu5323 给出左右边界,求出线段树最小的大小(暴力dfs搜索)
- Material Design之Material Menu侧滑菜单
- 洗牌算法
- hdu 3338 Kakuro Extension(最大流)