【编程学习记录】递归转非递归

想要知道怎么递归转非递归,就得先弄明白递归函数调用和返回的步骤(来源于网课):

调用

  • 保存调用信息(参数,返回地址)
  • 分配数据区(局部变量)
  • 控制转移给被调函数的入口

返回

  • 保存返回信息
  • 释放数据区
  • 控制转移到上级函数

因为递归满足LIFO,所以用栈来实现

递归转非递归方法

最机械的转换方法:

设置t+2个语句标号,t为函数递归调用的次数,令第0个标号为递归入口,1到t标记各个递归规则,t+1标记递归出口;通过入栈出栈来模拟实际递归时系统压栈弹栈的过程。最最机械的部分,则是真的用label在各处做标记,然后用goto语句模拟实现递归过程。

比如,拿求n的阶乘来举例子:

正常递归写法:

int factorial(int n)
{
	if (n == 1){
		return 1;
	} else 
		return n*factorial(n-1);
}

可以看到其中只有一个递归规则,只有一处递归调用,我们稍微转化一下可以得到:

void factorial(int n, int& f)
{
	int u;
	if (n == 1){
		f = 1;
	} else{
		factorial(n-1, u);
		f = n*u;
	}
}

即:

f(n)=\left\{\begin{matrix} 1 & n=1\\ n*f(n-1)&n>1 \end{matrix}\right.

开始设置标号:

  • label 0 :第一个可执行语句
  • label t+1 :设在函数体结束处
  • label i (1<=i<=t): 第i个递归返回处

代码如下:

先创建一个结构体:

struct fac
{
	int rd;//标号
	int pn;//n
	int pf;//f的值
	int u;//u的值
};
void factorial(int n, int& f)
{
	fac x, tmp;
	x.rd = 2;
	x.pn = n;
	S.push(x);//压入栈底作为监视哨
	label0: if ((x = S.top()).pn == 1){//递归入口
			S.pop();
			x.pf = 1;
			S.push(x);
			goto label2;
	}
	//第一处也是本代码唯一一处递归
	x.rd = 1;
	x.pn = x.pn-1;
	S.push(x);
	goto label0;
	label1: tmp = S.top(); S.pop();
			x = S.top(); S.pop();
			x.u = tmp.pf;
			x.pf = x.pn * x.u;
			S.push(x);
	//递归出口
	label2: x = S.top();
			switch (x.rd){
				case 0: goto label0; break;
				case 1: goto label1; break;
				case 2: tmp = S.top();
						S.pop();
						f = tmp.pf;
						break;
			}
}

但是会有一些冗余的入栈操作,并且毕竟goto语句现在也不大推荐使用了,比较容易出问题,于是有了优化后的代码:

void factorial(int n, int& f)
{
	fac x, tmp;
	x.rd = 2;
	x.pn = n;
	S.push(x);//监视哨
	do{
		//入栈
		while ((x = S.top()).pn > 1){
			x.rd = 1;
			x.pn = x.pn-1;
			S.push(x);
		}
		x = S.top();
		S.pop();
		x.pf = 1;
		S.push(x);
		//开始上升
		while ((x = S.top()).rd == 1){
			tmp = S.top(); S.pop();
			x = S.top(); S.pop();
			x.pf = x.pn * tmp.pf;
			S.push(x);
		}
	}while ((x = S.top()).rd != 2);
	S.pop();
	f = x.pf;
}

有两处递归调用的程序也类似,可以选择从某一个递归语句开始入栈,从另一个上升,大家自己去摸索吧!溜了溜了

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值