递 归
冯文科
一、递归的基本概念。
一个函数、概念或数学结构,如果在其定义或说明内部直接或间接地出现对其本身的引用,或者是为了描述问题的某一状态,必须要用至它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自己来定义自己的方法,称之为递归或递归定义。在程序设计中,函数直接或间接调用自己,就被称为递归调用。
二、递归的最简单应用:通过各项关系及初值求数列的某一项。
在数学中,有这样一种数列,很难求出它的通项公式,但数列中各项间关系却很简单,于是人们想出另一种办法来描述这种数列:通过初值及与前面临近几项之间的关系。
要使用这样的描述方式,至少要提供两个信息:一是最前面几项的数值,一是数列间各项的关
系。
比如阶乘数列
1、2、6、24、120、720……
如果用上面的方式来描述它,应该是:
如果需要写一个函数来求的值,那么可以很容易地写成这样:
int f(int n)
{
if(n==1)
return 1;
return n*f(n-1);
}
这就是递归函数的最简单形式,从中可以明显看出递归函数都有的一个特点:先处理一些特殊情况——这也是递归函数的第一个出口,再处理递归关系——这形成递归函数的第二个出口。
递归函数的执行过程总是先通过递归关系不断地缩小问题的规模,直到简单到可以作为特殊情况处理而得出直接的结果,再通过递归关系逐层返回到原来的数据规模,最终得出问题的解。
以上面求阶乘数列的函数为例。如在求时,由于3不是特殊值,因此需要计算,但是对它自己的调用,于是再计算,2也不是特殊值,需要计算,需要知道的值,再计算,1是特殊值,于是直接得出,返回上一步,得,再返回上一步,得,从而得最终解。
用图解来说明,就是
下面再看一个稍复杂点的例子。
【例1】数列的前几项为
1、、、、……
输入,编程求的精确分数解。
分析:
这个题目较易,发现,其它情况下有。如要求实数解的话,这基本已经可以写出递归函数了。但由于题目要求精确的分数解,还需做一些调整。设,则由递归关系,有,再约分化简,即得。但发现一个问题:求出时,需要返回两个整数:分子与分母,而通常的函数只能返回一个整数。
这个问题一般有两类解决办法,一种是让求值函数返回一个结构体变量,这样就可以返回两个变量了(其实还可以不只两个呢);另一种是在求值函数的参数表中加入两个指针变量或引用变量,通过参数给带回数值。但由于后一种做法会使程序结构不清晰——返回值是由参数表得到的,因此我们使用前一种方法。
另外,在通过得出后,就已经是最简分数了,无须化简。证明如下:
若是最简分数,即说明的最大公约数为1,即对任何,都有与不全为0,不防记、,则有
只要与不全为0,且,就有与不全为0。因此对任何的,有与不全为0。
而对于的情况而言,记,则有
由于,因此同样有与不全为0。
所以对任意字符串函数应用详解,都有与不全为0,因此它们的最大公约数为1,即是最简分数。虽然这是个要求(即)是最简分数的结论,但由于数列第二项为,是最简分数,因此可以证明第三项也是最简分数,同时也证明对所有的,求出的就是最简分数,无须化简。
具体代码如下:
, MAX*sizeof(char));
t[n]='\0';
for(i=0;i<n;i++)
{
t[q[i]]='Q';
cout<<t<<endl;
t[q[i]]='.';
}
cout<<endl;
}
bool test(int i, int k)
{
int j;
j=0;
while(j<k && abs(j-k)!=abs(q[j]-i))
j++;
if(j==k && mark[i]==false)
return true;
else
return false;
}
void search(int k)
{
if(k==n)
{
write();
c++;
return;
}
int i;
for(i=0;i<n;i++)
if(test(i, k))
{
mark[i]=true;
q[k]=i;
search(k+1);
mark[i]=false;
}
}
int main()
{
cin>>n;
memset(mark, 0, MAX*sizeof(bool));
c=0;
search(0);
cout<<c<<endl;
system("pause");
return 0;
}
六、练习
【练习】为给定的表达式建立表达式树,并求值。给定的表达式中,所有数字都是1位正整数,出现的符号可能为+、-、*、/、(、)。
分析:
这是一个与一般数据结构书上讲的用栈计算的方法本质不同的方法。
在详细说明这个算法之前,需要首先明确这个算法用到的概念
1、单元:一个单元可能是用括号括起来的一个表达式,或是一个整数;
2、项:一个项是指由*与/连接起来的若干单元;
3、表达式:一个表达式是指由+或-连接起来的若干项。
要建立表达式树,需要三个函数互相调用的函数:一个是getunit,用于建立一个单元;一个是getexpr,用于建立一个项,另一个就是build,用于建立一个表达式。
getunit函数较易,如果字符串首字母是(的话,那么从它后面的字符开始用build建立一个表达式,这个表达式就是一个单元;否则,就处理一个整数;
getexpr函数是建立在getunit之上的,它先用getunit建立一个单元,然后不停地考察之后地连接符号是不是*或/,若是,则不停地重复读连接符、建立另一个单元、建立连接的操作,直到连接符号不是*或/为止。
build函数是用于最终建立表达式的,它先用getexpr建立一个项,再用符号将剩余的各项连接成二叉树。
代码如下:
if(n>0){ hanoi(n-1,x,z,y); hanoi(n-1,y,x,z); }.w[10]中
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论