在定义一个过程或函数时出现调用本过程或本函数的成分,称之为递归。若调用自身,称之为直接递归。若过程或函数p调用过程或函数q,而q又调用p,称之为间接递归。
例 求n!(n为正整数) 。
一般的方法:
n! = 1*2*…*(n-1)*n
递归的方法:
int factorial(int n)
{ int x;
if (n==0)
x=1;
else
x=factorial (n-1)*n;
return(x);
}
在该函数factorial (n)求解过程中,直接调用factorial (n-1)自身,所以它是一个直接递归函数。
递归模型是递归算法的抽象,它反映递归问题的递归结构,例如,前面的递归算法对应的递归模型如下:
fun(0)=1 n=0 (1) fun(n)=n*fun(n-1) n>0 (2)
其中:第一个式子给出了递归的终止条件,我们称之为递归出口;第二个式子给出了fun(n)的值与fun(n-1)的值之间的关系,我们称之为递归体。
递归算法有两个基本特性:一是递归算法是一种分而治之的、把复杂问题分解为简单问题的求解问题方法,对求解某些复杂问题,递归算法分析问题的方法是十分有效的;二是递归算法的时间/空间效率通常比较差。
因此,对求解某些问题时,我们希望用递归算法分析问题,用非递归算法具体求解问题。这就需要把递归算法转换为非递归算法。
把递归算法转化为非递归算法有如下三种基本方法:
(1)通过分析,跳过分解过程,直接用循环结构的算法实现求值过程。
(2)自己用栈模拟系统的运行时栈,通过分析只保存必须保存的信息,从而用非递归算法替代递归算法。
(3)利用栈保存参数,由于栈的后进先出特性吻合递归算法的执行过程,因而可以用非递归算法替代递归算法。
采用循环结构消除递归。求解Fibonacci数列的算法如下:
int Fib(int n)
{ int i,f1,f2,f3;
if (n==1 || n==2) return(n);
f1=1;f2=2;
for (i=3;i<=n;i++)
{ f3=f1+f2;
f1=f2;f2=f3;
}
return(f3);
}
下面讨论直接使用栈保存中间结果,从而将递归算法转化为非递归算法的过程。
仍以例6.1的递归算法进行讨论,其递归模型有一个递归出口和一个递归体两个式子,分别称为(1)式和(2)式。设计一个栈,其结构如下:
struct
{ int vn; /*保存n值*/
int vf; /*保存fun1(n)值*/
int tag; /*标识是否求出fun1(n)值,1:未求出,0:已求出*/
} St[MaxSize]; /*定义栈*/
计算fun1(5)之值的过程如下:
将(5,*,1)进栈;
while (栈不空)
{ if (栈顶元素未计算出栈顶元素的vf值即St[top].tag==1)
{ if (栈顶元素满足(1)式)
求出对应的St[top].vf值,并置St[top].tag=0;
表示已求出对应的函数值;
else /*栈顶元素满足(2)式*/
将(St[top].vn-1,*,1)进栈;
}
else if (栈顶元素已计算出栈顶元素的vf值即St[top].tag==0)
显然栈顶元素由次栈顶元素通过(2)式分解得到的,回过来
由栈顶元素的vf值计算出次栈顶元素的vf值并退栈;
if (栈中只有一个已求出vf值的元素)
退出循环;
}
St[0].vf即为所求的fun1(n)值;
对应的非递归fun1算法如下:
int fun1(int n)
{ top++; /*初值进栈*/
St[top].vn=n; St[top].tag=1;
while (top>-1) /*栈不空时循环*/
{ if (St[top].tag==1) /*未计算出栈顶元素的vf值*/
{ if (St[top].vn==0) /*(1)式*/
{ St[top].vf=1;
St[top].tag=0;
}
else /*(2)式*/
{ top++;
St[top].vn=St[top-1].vn-1; St[top].tag=1;
}
}
else if (St[top].tag==0) /*已计算出vf值*/
{ St[top-1].vf=St[top-1].vn*St[top].vf; /*(2)式*/
St[top-1].tag=0;
top--;
}
/*栈中只有一个已求出vf的元素时退出循环*/
if (top==0 && St[top].tag==0)
break;
}
return(St[top].vf);
}