版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章
递归5.1什么是递归5.2递归算法的设计CONTENTS提纲1/61在定义一个过程或函数时出现调用本过程或本函数的成分,称之为递归。若调用自身,称之为直接递归。若过程或函数A调用过程或函数B,而B又调用A,称之为间接递归。在算法设计中,任何间接递归算法都可以转换为直接递归算法来实现,所以主要讨论直接递归。5.1.1递归的定义5.1什么是递归2/61【例5.1】以下是求n!(n为正整数)的递归函数。它属于什么类型的递归。intfun(intn){if(n==1) //语句1return(1); //语句2else //语句3return(fun(n-1)*n); //语句4}解:直接递归函数。3/61递归算法通常通常把一个大的复杂问题层层转化为一个或多个与原问题相似的规模较小的问题来求解。递归策略只需少量的代码就可以描述出解题过程所需要的多次重复计算,大大减少了算法的代码量。原问题小问题1小问题1小问题k…4/61一般来说,能够用递归解决的问题应该满足以下3个条件:需要解决的问题可以转化为一个或多个子问题来求解,而这些子问题的求解方法与原问题完全相同,只是在数量规模上不同。递归调用的次数必须是有限的。必须有结束递归的条件来终止递归。5/615.1.2何时使用递归1.定义是递归的
有许多数学公式、数列等的定义是递归的。例如,求n!和Fibonacci(斐波那契)数列等。intFib1(intn){//求Fibonacci数列的第n项if(n==1||n==2)return1;else
returnFib1(n-1)+Fib1(n-2);}6/612.数据结构是递归的有些数据结构是递归的。如单链表就是一种递归数据结构。classLinkNode<E>{
//单链表结点泛型类Edata;LinkNode<E>next; //下一个结点的指针publicLinkNode(){ //构造方法next=null;}publicLinkNode(Ed){ //重载构造方法data=d;next=null;}}head=(a1,head.next)a1an∧a2…headhead.next也是一个单链表不带头结点单链表7/61求一个不带头结点单链表p中所有data成员(假设为int型)之和。示例publicstaticintSum(LinkNode<Integer>p){if(p==null)return0;elsereturn(p.data+Sum(p.next));}a1an∧a2…pp.next8/613.问题的求解方法是递归的Hanoi问题设Hanoi(n,x,y,z)表示将n个盘片从x塔座借助y塔座移动到z塔座上:Hanoi(n,x,y,z)Hanoi(n-1,x,z,y);move(n,x,z):将第n个圆盘从x移到z;Hanoi(n-1,y,x,z)9/61publicstaticvoidHanoi(intn,charX,charY,charZ){if(n==1) //只有一个盘片的情况System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);else { //有两个或多个盘片的情况
Hanoi(n-1,X,Z,Y);System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);
Hanoi(n-1,Y,X,Z);}}10/61n=4结束11/615.1.3递归模型递归模型是递归算法的抽象,它反映一个递归问题的递归结构。intfun(intn){if(n==1) //语句1return(1); //语句2else //语句3return(fun(n-1)*n); //语句4}f(n)=1 n=1f(n)=n*f(n-1) n>1递归模型12/61f(n)=1 n=1f(n)=n*f(n-1) n>1递归出口递归体一般地,一个递归模型是由递归出口和递归体两部分组成。递归出口确定递归到何时结束,即指出明确的递归结束条件。递归体确定递归求解时的递推关系。13/61递归出口的一般格式如下:递归体的一般格式如下:f(s1)=m1f(sn)=g(f(si),f(si+1),…,f(sn-1),cj,cj+1,…,cm)f(sn)f(si)f(si+1)f(sn-1)…大问题求解若干个相似子问题求解转化非递归函数14/615.1.4递归与数学归纳法采用数学归纳法证明1+2+…+n=n(n+1)/2当n=1时,左式=1,右式=(1×2)/2=1,左右两式相等,等式成立。假设当n=k-1时等式成立,有1+2+…+(k-1)=k(k-1)/2。当n=k时,左式=1+2+…+k=[1+2+…+(k-1)+k=k(k-1)/2+k=k(k+1)/2。先考虑特殊情况。然后假设n=k-1成立(第二数学归纳法是假设n≤k-1均成立),再证明n=k时成立,即假设“小问题”成立,再推导出“大问题”成立。15/61先考虑特殊情况。然后假设n=k-1成立(第二数学归纳法是假设n≤k-1均成立),再证明n=k时成立,即假设“小问题”成立,再推导出“大问题”成立。递归出口递归体递归出口相当于数学归纳法的特殊情况。递归体相当于数学归纳法的归纳步骤。区别:数学归纳法是一种论证方法,递归是算法和程序设计的一种实现技术。数学归纳法是递归求解问题的理论基础。16/61简化的递归模型5.1.5递归的执行过程f(s1)=m1
f(sn)=g(f(sn-1),cn-1)f(sn)↓
f(sn-1)↓
…↓f(s2)↓f(s1)求f(sn)的分解过程如下:求大问题f(sn):分解(递推)和其值17/61遇到递归出口发生“质变”,原递归问题便转化成可以直接求解的问题。求值过程:f(s1)=m1↓
f(s2)=g(f(s1),c1)↓f(s3)=g(f(s2),c2)↓…↓f(sn)=g(f(sn-1),cn-1)18/61例如求5!。fun(5)fun(4)fun(3)fun(2)fun(1)r返回1fun(2)=2fun(3)=6fun(4)=24fun(5)=120分解过程求值过程19/61系统内部如何执行递归算法一个递归函数的调用过程类似于多个函数的嵌套的调用,只不过调用函数和被调用函数是同一个函数。为了保证递归函数的正确执行,系统需设立一个工作栈。
(1)执行开始时,首先为递归调用建立一个工作栈,其结构包括值参、局部变量和返回地址。
(2)每次执行递归调用之前,把递归函数的值参和局部变量的当前值以及调用后的返回地址进栈。
(3)每次递归调用结束后,将栈顶元素出栈,使相应的值参和局部变量恢复为调用前的值,然后转向返回地址指定的位置继续执行。20/61示例例如,有以下程序段:publicstaticintS(intn){return(n<=0)?0:S(n-1)+n;}publicstaticvoidmain(String[]args){System.out.printf("%d\n",S(1));}
程序执行时使用一个栈来保存调用过程的信息,这些信息用main()、S(0)和S(1)表示,那么自栈底到栈顶保存的信息的顺序是怎么样呢?21/61main()S(1)S(0)栈顶栈底执行过程:
调用main()
调用S(1)
调用S(0)
从S(0)返回
从S(1)返回
从main()返回publicstaticintS(intn){return(n<=0)?0:S(n-1)+n;}publicstaticvoidmain(String[]args){System.out.printf("%d\n",S(1));}22/61用递归算法的形参值表示状态,由于递归算法执行中系统栈保存了递归调用的值参、局部变量和返回地址。所以在递归算法中一次递归调用后会自动恢复该次递归调用前的状态。publicstaticvoidf(intn){if(n==0) //递归出口return;else { //递归体System.out.println("Pre:n="+n);System.out.printf("执行f(%d)\n",n-1);
f(n-1);System.out.println("Post:n="+n);}}23/61publicstaticvoidf(intn){if(n==0)return; //递归出口else { //递归体System.out.println("Pre:n="+n);System.out.printf("执行f(%d)\n",n-1);
f(n-1);System.out.println("Post:n="+n);}}执行f(4)的结果Pre:n=4执行f(3) //递归调用f(3)Pre:n=3执行f(2) //递归调用f(2)Pre:n=2执行f(1) //递归调用f(1)Pre:n=1执行f(0) //递归调用f(0)Post:n=1 //恢复f(0)调用前的n值Post:n=2 //恢复f(1)调用前的n值Post:n=3 //恢复f(2)调用前的n值Post:n=4
//恢复f(3)调用前的n值24/615.1.6递归算法的时空分析递归算法执行过程不同于非递归算法,所以其时空分析也不同于非递归算法。非递归算法分析是定长时空分析。递归算法分析就是变长时空分析。25/611.递归算法的时间分析publicstaticvoidHanoi(intn,charX,charY,charZ){if(n==1) //只有一个盘片的情况System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);else { //有两个或多个盘片的情况
Hanoi(n-1,X,Z,Y);System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);
Hanoi(n-1,Y,X,Z);}}执行Hanoi(n,x,y,z)的时间复杂度为O(1)吗??26/61publicstaticvoidHanoi(intn,charX,charY,charZ){if(n==1) //只有一个盘片的情况System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);else { //有两个或多个盘片的情况
Hanoi(n-1,X,Z,Y);System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);
Hanoi(n-1,Y,X,Z);}}设大问题Hanoi(n,x,y,z)的执行时间为T(n),则小问题Hanoi(n-1,x,y,z)的执行时间为T(n-1)。递推式:T(n)=1 当n=1时T(n)=2T(n-1)+1 当n>1时27/61T(n)=1 当n=1时T(n)=2T(n-1)+1 当n>1时T(n)=2T(n-1)+1=2(2T(n-2)+1)+1=22T(n-2)+2+1=22(2T(n-3)+1)+2+1=23T(n-3)+22+2+1=…=2n-1T(1)+2n-2+…+22+2+1=2n-1=O(2n)28/612.递归算法的空间分析publicstaticvoidHanoi(intn,charX,charY,charZ){if(n==1) //只有一个盘片的情况System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);else{ //有两个或多个盘片的情况
Hanoi(n-1,X,Z,Y);System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);
Hanoi(n-1,Y,X,Z);}}执行Hanoi(n,x,y,z)的空间复杂度为O(1)吗??29/61publicstaticvoidHanoi(intn,charX,charY,charZ){if(n==1) //只有一个盘片的情况System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);else{ //有两个或多个盘片的情况
Hanoi(n-1,X,Z,Y);System.out.printf("将第%d个盘片从%c移动到%c\n",n,X,Z);
Hanoi(n-1,Y,X,Z);}}设大问题Hanoi(n,x,y,z)的占用空间为S(n),则小问题Hanoi(n-1,x,y,z)的占用空间为S(n-1)。递推式:S(n)=1 当n=1时S(n)=S(n-1)+1 当n>1时30/61S(n)=S(n-1)+1=S(n-2)+1+1=S(n-2)+2=…=S(1)+(n-1)=1+(n-1)=n=O(n)S(n)=1 当n=1时S(n)=S(n-1)+1 当n>1时31/61确定问题规模n确定终止情况确定递推情况递推式由递推式求出T(n)/S(n)用复杂度表示T(n)/S(n)递归算法分析32/615.2递归算法的设计5.2.1递归算法设计的步骤设计求解问题的递归模型。转换成对应的递归算法。递归模型递归算法33/61(1)对原问题f(s)进行分析,称为“大问题”,假设出合理的“小问题”f(s’);
求递归模型的步骤如下:(3)确定一个特定情况(如f(1)或f(0))的解
递归出口。(2)假设f(s’)是可解的,在此基础上确定f(s)的解,即给出f(s)与f(s’)之间的关系
递归体。数学归纳法假设n=k-1时等式成立求证n=k时等式成立求证n=1时等式成立34/61【例5.2】采用递归算法求整数数组a[0..n-1]中的最小值。假设f(a,i)求数组元素a[0..i](共i+1个元素)中的最小值。当i=0时,有f(a,i)=a[0]。假设f(a,i-1)已求出,显然有f(a,i)=MIN(f(a,i-1),a[i]),其中MIN()为求两个值较小值函数。得到如下递归模型:f(a,i)=a[0] 当i=0时f(a,i)=MIN(f(a,i-1),a[i]) 其他情况35/61f(a,i)=a[0] 当i=0时f(a,i)=MIN(f(a,i-1),a[i]) 其他情况publicstaticintMin(int[]a,inti){if(i==0) //递归出口returna[0];else{ //递归体intmin=Min(a,i-1);if(min>a[i])return(a[i]);elsereturnmin;}}36/615.2.2基于递归数据结构的递归算法设计递归数据结构的数据特别适合递归处理递归算法种瓜得瓜:递归性数据:D={瓜的集合}运算:Op={种瓜}递归性:Op(x∈D)∈D37/2237/61【例5.3】假设有一个不带头结点的单链表p,完成以下两个算法设计:(1)设计一个算法正向输出所有结点值。(2)设计一个算法反向输出所有结点值。a1an∧a2…pp.next大问题:f(p)输出a1到anf(p.next)输出a2到an为什么在这里设计单链表的递归算法时不带头结点?如何将带头结点转换为不带头结点的单链表?38/61a1an∧a2…pp.next大问题:f(p)输出a1到anf(p.next)输出a2到anf(p)
不做任何事件
当p=null时f(p)
输出p结点值;f(p.next) 其他情况publicstaticvoidPositive(LinkNode<Integer>p){if(p==null)return;else{System.out.print(p.data+"");
Positive(p.next);}}(1)正向输出39/61a1an∧a2…pp.next大问题:f(p)输出an到a1f(p.next)输出an到a2f(p)
不做任何事件
当p=null时f(p)
f(p.next);输出p结点值
其他情况publicstaticvoidReverse(LinkNode<Integer>p){if(p==null)return;else{
Reverse(p.next);System.out.print(p.data+"");}}(2)反向输出40/61publicstaticvoidPositive(LinkNode<Integer>p){if(p==null)return;else{System.out.print(p.data+"");
Positive(p.next);}}比较publicstaticvoidReverse(LinkNode<Integer>p){if(p==null)return;else{
Reverse(p.next);System.out.print(p.data+"");}}41/615.2.3基于归纳方法的递归算法设计通过对求解问题的分析归纳来转换成递归方法求解(如皇后问题等)。关键是对问题本身进行分析,确定大、小问题解之间的关系,构造合理的递归体,而其中最重要的又是假设出“合理”的小问题。42/61【例5.4】若算法pow(x,n)用于计算xn(n为大于1的整数)。完成以下任务:
(1)采用递归方法设计pow(x,n)算法。
(2)问执行pow(x,5)发生几次递归调用?求pow(x,n)对应的算法复杂度是多少?
(3)设计相应的非递归算法pow1(x,n)。43/61(1)设f(x,n)用于计算xn,则有以下递归模型:f(x,n)=1 当n=0f(x,n)=x*f(x,n/2)*f(x,n/2) 当n为奇数f(x,n)=f(x,n/2)*f(x,n/2) 当n为偶数publicstaticdoublepow(doublex,intn){if(n==0)return1;doublep=pow(x,n/2);if(n%2==1)returnx*p*p; //n为奇数elsereturnp*p; //n为偶数}44/61(2)执行pow(x,5)的递归调用顺序是:pow(x,5)→pow(x,2)→pow(x,1)→pow(x,0)
共发生4次递归调用。求pow(x,n)对应的算法复杂度是O(log2n)。45/61
(3)为了计算xn,可以将n拆成二进制数,该二进制数第i位的权为2i-1。
例如当n=11时,其二进制数是[1011]2
实现:ans=1,base=x,从低到高取n的每个二进制位(n&1),若为0跳过,若为1,ans*=base,n=n>>1。1
101n=11:各位二进制权:232221208421各位的x权:x8x4x2x1x8×
x4×
x1xn:x权依次倍乘等于二进制1的x权相乘46/61publicstaticdoublepow1(doublex,intn){doubleans=1.0,base=x;while(n!=0){if((n&1)==1) //遇到二进制位1ans*=base;base*=base;n>>=1;}returnans;}大名鼎鼎的快速幂算法47/61【例5.5】创建一个n阶螺旋矩阵并输出。例如,n=4时的螺旋矩阵如下: 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 748/61设f(x,y,start,n)用于创建左上角为(x,y)、起始元素值为start的n阶螺旋矩阵,共n行n列,它是大问题。f(x+1,y+1,start,n-2)用于创建左上角为(x+1,y+1)、起始元素值为start的n-2阶螺旋矩阵,共n-2行n-2列,它是小问题。12341213145111615610987f(0,0,1,4)为大问题f(1,1,13,2)为小问题参考答案49/61对应的递归模型如下:f(x,y,start,n)
不做任何事情
当n≤0f(x,y,start,n)
产生只有一个元素的螺旋矩阵
当n=1f(x,y,start,n)
产生(x,y)的那一圈;
当n>1
f(x+1,y+1,start,n-2)12341213145111615610987f(0,0,1,4)为大问题f(1,1,13,2)为小问题50/61voidSpiral(intx,inty,intstart,intn){//递归创建螺旋矩阵inti,j;if(n<=0)return; //递归结束条件if(n==1){ //矩阵大小为1时s[x][y]=start;return;}for(i=x;i<x+n-1;i++) //上一行s[y][i]=start++;for(j=y;j<y+n-1;j++) //右一列
s[j][x+n-1]=start++;for(i=x+n-1;i>x;i--) //下一行s[y+n-1][i]=start++;for(j=y+n-1;j>y;j--) //左一列s[j][x]=start++;
Spiral(x+1,y+1,start,n-2); //递归调用}51/61【例5.6】采用递归算法求解迷宫问题,并输出从入口到出口的所有迷宫路径。求解问题描述:
(xi,yi)(xe,ye)mgpath(xi,yi,xe,ye,path)出口mgpath(intxi,intyi,intxe,intye,Boxpath):求从(xi,yi)到(xe,ye)的迷宫路径,用path变量保存迷宫路径。入口52/61(xi,yi)(xe,ye)mgpath(xi,yi,xe,ye,path)出口大问题入口(i,j)(xi,yi)走一步(xe,ye)mgpath(i,j,xe,ye,path)出口小问题大问题≡走一步+小问题入口53/61mgpath(xi,yi,xe,ye,path)
将(xi,yi)添加到path中; 置mg[xi][yi]=-1;
输出path中的迷宫路径;
恢复出口迷宫值为0即置mg[xe][ye]=0
若(xi,yi)=(xe,ye)即找到出口mgpath(xi,yi,xe,ye,path)
将(xi,yi)添加到path中; 置mg[xi][yi]=-1;
对于(xi,yi)每个相邻可走方块(i,j),
调用mg(i,j,xe,ye,path);path回退一步并置mg[xi][yi]=0;
若(xi,yi)不是出口求解迷宫问题的递归模型如下:54/61importjava.lang.*;importjava.util.*;classBox{
//方块类inti; //方块的行号intj; //方块的列号publicBox(inti1,intj1){ //构造方法i=i1;j=j1;}}55/61classMazeClass{
//求解迷宫路径类finalintMaxSize=20;int[][]mg; //迷宫数组intm,n; //迷宫行列数intcnt=0; //累计迷宫路径数publicMazeClass(intm1,intn1){ //构造方法m=m1;n=n1;mg=newint[MaxSize][MaxSize];}publicvoidSetmg(int[][]a){ //设置迷宫数组for(inti=0;i<m;i++)for(intj=0;j<n;j++)mg[i][j]=a[i][j];}56/61voidmgpath(intxi,intyi,intxe,intye,ArrayList<Box>path){
//求解迷宫路
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年商洛市镇安县鼎丰矿业有限公司招聘(8人)笔试参考题库及答案详解
- 2026年“黑龙江人才周”黑龙江工程学院公开招聘事业编制博士教师36人笔试参考题库及答案详解
- 2026年白银市平川区城管协管人员招聘笔试参考试题及答案详解
- 2026重庆沙坪坝区歌乐山街道遴选本土人才2人笔试模拟试题及答案详解
- 2026重庆市开州区中医院公开招聘临聘人员11名笔试备考题库及答案详解
- 2026年海南高考地理试卷真题及答案详解(精校打印版)
- 2026年11月02日 阳江市江城区考核基地 赢创工业 特种化工工程师 13人
- 吉林省长春市榆树市部分学校2027届九年级上学期开学阶段学情自测物理试卷(含答案)
- 贵州省六盘水市盘州市2025-2026学年大象版四年级下学期期末学生综合素养评价科学试卷(有答案)
- 2026-2027学年广西南宁市第四十七中学八年级(上)开学评估物理试卷(含答案)
- 2026年全国保密教育线上培训考试题(含答案)
- 2026年电力负荷预测的技术方法
- 英语A级高频词汇
- 2026全国第二届班组长大赛(国防赛道)初赛理论参考题库(含答案)
- 2026年贵州中考数学真题及答案
- 2026世界人工智能大会暨人工智能全球治理高级别会议全量演讲稿
- 核电站安保管理流程及标准
- 2026年秋季学期苏教版新版六年级上册科学教学计划含教学进度表
- 2026秋新版统编版小学语文五年级上册教学设计(附目录)适用于新课标
- 2026年高考语文真题全国Ⅱ卷《打橘子》详尽解析
- 道路标线监理实施细则
评论
0/150
提交评论