您的位置:首页 > 其它

递归函数的非递归化

2015-08-02 21:27 204 查看
昨天做usaco的一道题,把递归函数非递归化看看对效率的提升,对如何转化,做了一些尝试。

理解递归过程

递归是函数调用自己本身,每调用一次自己,就进入一个新的堆栈帧



非递归化

通过观察递归的过程,我们知道这是通过堆栈来实现的,那么非递归化,就是

我们手动构造一个堆栈,在一次函数过程中模拟递归。

既然我们构造堆栈,堆栈要保存什么信息?

保存函数的局部变量,及执行到的语句。


堆栈的维护

每调用一次函数,相对应就要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));

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: