您好,欢迎访问三七文档
1递归2递归算法在可计算性理论中占有重要地位,它是算法设计的有力工具,对于拓展编程思路非常有用。就递归算法而言并不涉及高深数学知识,只不过初学者要建立起递归概念不十分容易。我们先从一个最简单的例子导入。递归及其实现用递归算法求n!定义:函数fact(n)=n!fact(n-1)=(n-1)!则有fact(n)=nfact(n-1)已知fact(1)=13为了表述得直观清晰,我们定义两个结点:或结点和与结点。图示的直观性与思维助力。1、或结点,,BZtrueCZfalseA(真)(假)A条件Z条件!ZBCA为“或结点”,A依不同条件会有两种不同的取值,B或C。结点用表示。4如果有多于2种取值,可用下图:Z1Z2…ZnBC…GA条件为Z1,Z2,…,Zn,A取值为B或C,…或G52、与结点与结点要涂黑,相关联的B与C之间要用弧线连起来。A为与结点,A的最终取值为C结点的值,但为了求得C的值,得先求出B结点的值,C是B的函数。ABC6仍以求n!为例画出如下与或图A为或结点;B为直接可解结点,值为1;C为与结点,当n1时,A的取值即C的值,而C的值即E的值,为了求得E的值,需要先求出D的值。D值fact(n-1)乘以n即为E的值。Afact(n)Bfact(1)=1Cn1n=1Dfact(n-1)En*fact(n-1)7与结点可能有多个相关联的点,这时可描述为下图A结点的值最终为D的值,但为了求D需先求B和C。从图上看,先求左边的点才能求最右边的点的值,我们约定最右边D点的值就是A结点的值。ABDC8下面我们以3!为例来画与或结点图,目的是体会递归的含义。C=1D=2*C=2B=D=2E=3*B=3*2=6A=E=61=1131Bfact(2)3*fact(2)fact(3)Cfact(1)D2*fact(1)AE219下面画出了调用和返回的递归示意图Bfact(2)=2*fact(1)=2*1=2返回DAfact(3)=3*fact(2)=3*2=6E返回Cfact(1)=1调用调用10从图可以想象:欲求fact(3),先要求fact(2);要求fact(2)先求fact(1)。就象剥一颗圆白菜,从外向里,一层层剥下来,到了菜心,遇到1的阶乘,其值为1,到达了递归的边界。然后再用fact(n)=n*fact(n-1)这个普遍公式,从里向外倒推回去得到fact(n)的值。为了把这个问题说得再透彻一点。我们画了如下的流程图:113==1fact(3)真假调用fact(2)计算3*fact(2)fact(2)fact(1)2==1真假调用fact(1)计算2*fact(1)1==1真假fact(1)=1返回返回12为了形象地描述递归过程,将上图改画成下图fact(3)真假3==1调用fact(2)真假假真2==11==1fact(2)=2*fact(1)返回fact(3)=3*fact(2)返回调用fact(1)fact(1)=1返回13在这个图中“内层”与“外层”有着相同的结构。它们之间“你中有我,我中有你”,呈现相互依存的关系。为了进一步讲清递归的概念,将递归与递推做一比较。仍以求阶乘为例。递推是从已知的初始条件出发,逐次去求所需要的阶乘值。如求3!初始条件fact(1)=1fact(2)=2*fact(1)=2fact(3)=3*fact(2)=614这相当于从菜心“推到”外层。递归算法的出发点不放在初始条件上,放在求解的目标上,从所求的未知项出发逐次调用本身的求解过程,直到递归的边界(即初始条件)。就本例而言,读者会认为递归算法可能是多余的,费力而不讨好。但许多实际问题不可能或不容易找到显而易见的递推关系,这时递归算法就表现出了明显的优越性。下面我们将会看到,递归算法比较符合人的思维方式,逻辑性强,可将问题描述得简单扼要,具有良好的可读性,易于理解.许多看来相当复杂,或难以下手的问题,如果能够使用递归算法就会使问题变得易于处理。下面举一个尽人皆知的例子哈诺(Hanoi)塔问题。15故事:相传在古代印度的Bramah庙中,有位僧人整天把三根柱子上的金盘倒来倒去,原来他是想把64个一个比一个小的金盘从一根柱子上移到另一根柱子上去。移动过程中恪守下述规则:每次只允许移动一只盘,且大盘不得落在小盘上面。有人会觉得这很简单,真的动手移盘就会发现,如以每秒移动一只盘子的话,按照上述规则将64只盘子从一个柱子移至另一个柱子上,所需时间约为5800亿年。16怎样编写这种程序?思路上还是先从最简单的情况分析起,搬一搬看,慢慢理出思路。1、在A柱上只有一只盘子,假定盘号为1,这时只需将该盘从A搬至C,一次完成,记为move1#fromAtoC(演示)ABC1172、在A柱上有二只盘子,1为小盘,2为大盘。第(1)步将1号盘从A移至B,这是为了让2号盘能动;第(2)步将2号盘从A移至C;第(3)步再将1号盘从B移至C;这三步记为(演示):ABC21move1#fromAtoB;move2#fromAtoC;move1#formBtoC;183、在A柱上有3只盘子,从小到大分别为1号,2号,3号第(1)步将1号盘和2号盘视为一个整体;先将二者作为整体从A移至B,给3号盘创造能够一次移至C的机会。这一步记为move(2,A,C,B)意思是将上面的2只盘子作为整体从A借助C移至B。第(2)步将3号盘从A移至C,一次到位。记为move3#fromAtoC第(3)步处于B上的作为一个整体的2只盘子,再移至C。这一步记为move(2,B,A,C)意思是将2只盘子作为整体从B借助A移至C。所谓借助是什么意思,等这件事做完了不言自明。19move(3,A,B,C)move(2,A,C,B)move(1,A,B,C)move(2,B,A,C)ABC213演示:移动3个盘子的分解20move(3,A,B,C)move(2,A,C,B)move(1,A,B,C)move(1,A,C,B)move(1,C,A,B)move(1,A,B,C)move(2,B,A,C)move(1,B,C,A)move(1,B,A,C)move(1,A,B,C)ABC213214、从题目的约束条件看,大盘上可以随便摞小盘,相反则不允许。在将1号和2号盘当整体从A移至B的过程中move(2,A,C,B)实际上是分解为以下三步第(1).1步:move1#formAtoC;第(1).2步:move2#formAtoB;第(1).3步:move1#formCtoB;22经过以上步骤,将1号和2号盘作为整体从A移至B,为3号盘从A移至C创造了条件。同样,3号盘一旦到了C,就要考虑如何实现将1号和2号盘当整体从B移至C的过程了。实际上move(2,B,A,C)也要分解为三步:第(3).1步:move1#formBtoA;第(3).2步:move2#formBtoC;第(3).3步:move1#formAtoC;235、看move(2,A,C,B)是说要将2只盘子从A搬至B,但没有C是不行的,因为第(1).1步就要将1#盘从A移到C,给2#盘创造条件从A移至B,然后再把1#盘从C移至B。看到这里就能明白借助C的含义了。因此,在构思搬移过程的参量时,要把3个柱子都用上。246、定义搬移函数move(n,A,B,C),物理意义是将n只盘子从A经B搬到C输出n:AtoCmove(n-1,A,C,B)move(n-1,B,A,C)move(n,A,B,C)考虑到前面已经研究过的(1)(2)(3)步,可以将搬移过程用如下的与或结点图表示。25这里用与或结点的含义是分解为(1)(2)(3)步。这3步是相关的,相互依存的,而且是有序的,从左至右执行。move(n,A,B,C)分解为3步(1)move(n-1,A,C,B)理解为将上面的n-1只盘子作为一个整体从A经C移至B;(2)输出n:AtoC,理解为将n号盘从A移至C,是直接可解结点;(3)Move(n-1,B,A,C)理解为将上面的n-1只盘子作为一个整体从B经A移至C。26这里显然是一种递归定义,当着解move(n-1,A,C,B)时又可想到,将其分解为3步:第1步:将上面的n-2只盘子作为一个整体从A经B到C,move(n-2,A,B,C);第2步:第n-1号盘子从A直接移至B,即n-1:AtoB;第3步:再将上面的n-2只盘子作为一个整体从C经A移至B,move(n-2,C,A,B);下面,我们还是以3只盘子为例画出递归的与或图。27这个图很象一颗倒置着的树,结点move(3,A,B,C)是树根,与结点是树的分枝,叶子都是直接可解结点。输出1:AtoC输出3:AtoCmove(2,A,C,B)move(2,B,A,C)move(3,A,B,C)输出2:AtoB输出1:CtoB输出1:BtoA输出2:BtoC输出1:AtoCmove(1,A,B,C)move(1,C,A,B)move(1,B,C,A)move(1,A,B,C)28move(3,A,B,C)move(2,A,C,B)输出3:AtoCmove(2,B,A,C)move(2,A,C,B)调用move(1,A,B,C)move(2,B,A,C)调用move(1,B,C,A)返回返回调用调用返回move(1,A,B,C)输出2:AtoBmove(1,C,A,B)move(1,B,C,A)输出2:BtoCmove(1,A,B,C)输出1:AtoC输出1:BtoA输出1:CtoB输出1:AtoC4132576调用调用返回返回调用move(1,C,A,B)move(1,A,B,C)29输出3:AtoC调用move(1,C,A,B)输出:1:CtoB输出:2:AtoBmove(1,C,A,B)调用move(1,A,B,C)输出1:AtoCmove(1,A,B,C)move(2,A,C,B)调用move(2,A,C,B)调用move(2,B,A,C)调用move(1,A,B,C)输出1:AtoC输出:2:BtoC调用move(1,B,C,A)输出1:BtoAmove(1,B,C,A)move(2,B,A,C)move(1,A,B,C)move(3,A,B,C)123456730//*******************************************//*程序:6_hanoi2002.cpp*//*作者:wuwh*//*编制时间:2002年10月13日*//*主要功能:用递归求解Hanoi问题*//*******************************************#includeiostream//预编译命令usingnamespacestd;intstep=1;//整型全局变量,预置1,步数voidmove(int,char,char,char);//声明要用到的被调用函数31intmain()//主函数{//主程序开始intn;//整型变量,n为盘数,cout请输入盘数n=;//提示信息cinn;//输入正整数ncout在3根柱子上移//输出提示信息n只盘的步骤为:endl;move(n,'a','b','c');//调用move函数return0;//主函数结束}32//以下函数是被主程序调用的函数//函数名:move//输入:m,整型变量,表示盘子数目//p,q,r为字符型变量,表示柱子标号//返回值:无voidmove(intm,charp,charq,charr){//自定义函数体开始if(m==1)//如果m为1,则为直接可解结点,{//直接可解结点,输出移盘信息cout[step]move1#fromptorendl;step++;//步数加1}else//如果不为1,则要调用move(m-1){move(m-1,p,r,q);//递归调用move(m-1)//直接可解结点,输出移盘信息cout[step]movem#fromptor
本文标题:递归
链接地址:https://www.777doc.com/doc-6073297 .html