(1)直接用循环结构的算法替代递归算法(直接转化法,不需要使用栈)
(2)用栈模拟系统的运行过程,通过分析只保存必须保存的信息,从而用非递归方法替代递归算法。(间接转化法,需要使用栈)
用循环结构替代递归过程:
采用循环结构消除递归这种直接转化法没有通用的转换算法,对于具体问题要深入分析对应的递归结构,设计有效的循环语句进行递归到非递归的转换。
直接转化法特别适合于尾递归。尾递归只有一个递归调用语句,而且是出于算法的最后。
比如求阶乘的算法
//递归实现
#include<stdio.h>
int fun(int n)
{
if(n==1)
return 1;
else
return fun(n-1)*n;
}
int main()
{
printf("%d",fun(3));
return 0;
}
用直接转换法转换成非递归的算法:
//非递归实现
#include<stdio.h>
int fun(int n)
{
int M = 1;
for(int i = 1; i <= n; i ++)
{
M*=i;
}
return M;
}
int main()
{
printf("%d",fun(3));
return 0;
}
直接转化法也适合于单向递归。单向递归指递归函数中虽然有一处以上的递归调用语句,但各次递归调用语句的参数只和主调用函数有关,相互之间参数无关,并且这些递归调用语句也和尾递归一样处于算法的最后。
求Fibonacci数列
//递归算法实现
int Fib(int n)
{
if(n == 1||n == 2)
return 1;
return Fib(n - 1)+Fib(n - 2);
}
//非递归算法实现
int Fib1(int n)
{
int i,f1,f2,f3;
if(n == 1||n == 2)
return 1;
f1=1,f2=1;
for(int i = 3; i <= n; i++)
{
f3 = f1 + f2;
f1 = f2;
f2 = f3;
}
return f3;
}
用栈消除递归过程
通常使用栈保存中间结果,从而将递归算法转化为非递归算法的过程。
在设计栈时,除了保存递归函数的参数等外,还要增加一个标志成员(tag),对于某个递归小问题f(s’),其值为1表示对应递归问题尚未求出,需进一步分解转换,为0表示对用递归问题已求出,需通过该结果求解大问题f(s)。
为了方便讨论,将地规模型分为等值关系和等价关系两种。
等值关系是指“大问题”的函数值等于“小问题”的函数值的某种运算结果。
例如求n!对应的递归模型就是等值关系。
#include<stdio.h>
#include<stack>
using namespace std;
struct NodeType//栈元素类型
{
int n;//保存n值
int f;//保存f(n)值
int tag;//标识是否求出f(n)值,1:未求出,0:已求出
};
int fun2(int n)
{
NodeType e, e1, e2;
stack<NodeType> st;
e.n = n;//现在要求n的阶乘,原始n
e.tag = 1;//还未求出
st.push(e);//初值进栈
while(!st.empty())//栈不空时循环
{
if(st.top().tag == 1)//未计算出栈顶元素的f值
{
if(st.top().n == 1)
{
st.top().f = 1;//直接赋予阶乘值
st.top().tag = 0;//标识已求出
}
else
{
e1.n = st.top().n - 1;//构建新元素e1,标识要求st.top()
e1.tag = 1;//标识还未求出
st.push(e1);//子任务进栈,下一次循环时处理
}
}
else //st.top().tag = 0,即已计算出栈顶元素的f值
{
e2 = st.top();
st.pop();//退栈e2
st.top().f = st.top().n *e2.f;//根据递推式
st.top().tag = 0;
}
if(st.size() == 1&& st.top().tag == 0)
break;
}
return st.top().f;
}
int main()
{
printf("%d",fun2(6));
}
等价关系是指“大问题”的求解过程转化为“小问题”求解而得到的,他们之间不是值的相等关系,而是解的等价关系。
例如,求梵塔问题对应的递归模型就是等价关系,也即是说,Hanoi(n,x,y,z)与Hanoi(n-1,x,z,y)、move(n,x,z)和Hanoi(n-1,y,x,z)是等价的。