C语言递归_精品文档.doc
《C语言递归_精品文档.doc》由会员分享,可在线阅读,更多相关《C语言递归_精品文档.doc(6页珍藏版)》请在冰豆网上搜索。
WangDing
C语言递归
1简介
递归,作为C语言最经典的算法之一,是一种非常有用的程序设计方法。
虽然用递归算法编写的程序结构清晰,具有很好的可读性,还往往使某些看起来不易解决的问题变得容易解决。
但在递归函数中,由于存在着自调用过程,程序控制反复进入其自身,使程序的分析设计有一定困难,致使很多初学者往往对递归迷惑不解,也在这上面花了不少的时间,却收效甚微。
那么,究竟什么是递归?
怎么实现递归呢?
所谓递归,简而言之就是在调用一个函数的过程中又直接或间接地调用该函数本身,以实现层次数据结构的查询和访问。
在函数中直接调用函数本身,称为直接递归调用。
在函数中调用其它函数,其它函数又调用原函数,这就构成了函数自身的间接调用,称为间接递归调用。
2条件
而采用递归方法来解决问题,必须符合以下三个条件:
1、可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。
说明:
解决问题的方法相同,调用函数的参数每次不同(有规律的递增或递减),如果没有规律也就不能适用递归调用。
2、可以应用这个转化过程使问题得到解决。
说明:
使用其他的办法比较麻烦或很难解决,而使用递归的方法可以很好地解决问题
3、必定要有一个明确的结束递归的条件。
说明:
一定要能够在适当的地方结束递归调用。
不然可能导致系统崩溃。
3求n!
问题
好知道是这样以后;我们来写一个众多教材上的程序:
使用递归的方法求n!
。
当n>1时,求n!
的问题可以转化为n*(n-1)!
的新问题。
比如n=4:
第一部分:
4*3*2*1n*(n-1)!
第二部分:
3*2*1(n-1)(n-2)!
第三部分:
2*1(n-2)(n-3)!
第四部分:
1(n-4)!
4-4=0,得到值1,结束递归。
源程序如下:
#include
intfac(intn)
{
intc;
printf("nowthenumberis%d",n);
getchar();
if(n==1||n==0)
c=1;
else
c=n*fac(n-1);
printf("nowthenumberis%dandthe%d!
is%d",n,n,c);
getchar();
returnc;
}
voidmain()
{
intn=4;
printf("resultis%d.\n",fac(n));
}
可以看到,加上两条printf()和getchar()语句后,可以察看各级调用及其中间答案,很清楚的看到程序的执行过程。
运行结果如图1所示,当主函数第一次调用fac()函数的时候,由于n=4不等于0和1,并不立即返回结果1,而是执行c=n*fac(n-1),用实参n-1(值为3)调用fac()函数自己,即递归调用fac(3)。
于是进入第二层调用fac(),这时也没有得出结果,继续用实参n-1(值为2)调用fac()函数自己。
同样经过第三层调用后进入第四层调用,这时候n=1,算出1!
=1,满足结束递归的条件,然后把得出的结果1返回给第三次调用的fac函数,得出2*1!
=2,然后把结果2返回给第二次调用的fac函数,得出3*2!
=6,最后第一次调用的fac函数根据第二次调用的返回值算出4!
=4*3!
=4*6=24,结束整个递归调用,得出最终结果并输出。
我们做事情,一般都是从头开始的,而递归却是从末尾开始的。
比如上面的函数,当n>1的时候,就只能求助于n-1,而(n-1)>1时,就求助于n-2,然后……直到(n-k)=1时,函数fac终于有了返回值1了,它再从头开始计算,然后一直算到n为止。
所以说,递归简直就是一个数学模型,它的工作过程就是自己调用自己。
4说明
以下是几点对递归的说明:
(1).当函数自己调用自己时,系统将自动把函数中当前的变量和形参暂时保留起来,在新一轮的调用过程中,系统为新调用的函数所用到的变量和形参开辟另外的存储单元(内存空间)。
每次调用函数所使用的变量在不同的内存空间。
(2).递归调用的层次越多,同名变量的占用的存储单元也就越多。
一定要记住,每次函数的调用,系统都会为该函数的变量开辟新的内存空间。
(3).当本次调用的函数运行结束时,系统将释放本次调用时所占用的内存空间。
程序的流程返回到上一层的调用点,同时取得当初进入该层时,函数中的变量和形参所占用的内存空间的数据。
(4).在开发过程中使用printf()和getchar()可以看到执行过程,并且可以在发现错误后停止运行。
很多人说所有递归问题都可以用非递归的方法来解决,能不用递归就不用递归。
但是对于一些比较复杂的递归问题用非递归的方法往往使程序变得十分复杂难以读懂,而函数的递归调用在解决这类问题时能使程序简洁明了有较好的可读性,因此很多问题用递归可很容易解决。
同时由于递归调用过程中,系统要为每一层调用中的变量开辟内存空间、要记住每一层调用后的返回点、要增加许多额外的开销,因此函数的递归调用通常会降低程序的运行效率(在许多情况下,速度的差别不太明显)。
5动物繁殖问题
我曾经碰到过这样一个动物繁殖问题:
若一头小母牛,从出生起第四个年头开始每年生一头母牛,按此规律,第n年时有多少头母牛?
如果不用递归函数来做,每当母牛到第4岁的时候才会生下一头小母牛,所以,每年增加的新的1岁小母牛都是上一年3岁的母牛加上4岁的母牛生下数量之和,分析过程如图2所示
给出程序如下:
#include
voidmain()
{
inti,n;
intf1=1,f2=0,f3=0,f4=0;
printf("pleaseinputhowmanyyearsitpast:
\n");
scanf("%d",&n);
for(i=2;i<=n;i++)
{
f4=f4+f3;
f3=f2;
f2=f1;
f1=f4;
}
printf("nowyouhave%dcattle!
\n",f1+f2+f3+f4);
while
(1)
{;}
}
程序虽然简短,但是可读性太差,不易理解。
那么如果用递归函数求此问题呢?
我们先写出函数表达式:
f(n)=f(n-1)+f(n-3)
为什么f(n)=f(n-1)+f(n-3)呢,请看:
f(n)-f(n-1)=f(n-3)
因为第n年要比n-1年多的牛,都是大于三岁的牛生的小牛,而f(n-3)正是那些在n年大于三岁的牛,然后它们在第n年生下相同数量的小牛。
源代码如下:
#include
intcattle(intct,intn)
{
if(n<4)
return(ct);
else
return(cattle(ct,n-1)+cattle(ct,n-3));
}
voidmain()
{
intct=1,n;
printf("pleaseinputhowmanyyearsitpast:
\n");
scanf("%d",&n);
printf("youhave%dcattlenow!
\n",cattle(ct,n));
while
(1)
{
;
}
}
运行结果如图3所示:
可见,递归函数的主要优点是可以把算法写的比使用非递归函数时更清晰更简洁,而且某些问题,特别是与人工智能有关的问题,更适宜用递归方法。
递归的另一个优点是,递归函数不会受到怀疑,较非递归函数而言,某些人更相信递归函数。
编写递归函数时,必须在函数的某些地方使用if语句,强迫函数在未执行递归调用前返回。
如果不这样做,在调用函数后,它永远不会返回,造成无穷递归。
在递归函数中不使用if语句,是一个很常见的错误。
此外,象汉诺塔问题就只能靠递归才能解决,但是现实中很多问题都是比较简单的,没有象汉诺塔那么复杂,我们只要会用递归编程来为我们解决一些问题就行了,所以就不必深究了。
6/6
Email:
572645517@
星期五,十月14,2022