开始,要学会递归,首先要了解一下递归调用步骤,
话不多说,先看几行代码,
#include<stdio.h>
#include<math.h>
int count=0;
void first(int n){
if(n==1){
count++;printf("**%d*%d\n",count,n);
}
else{
count++;printf("**%d*%d\n",count,n);
first(n-1); //语句(1)
printf("&^&^\n");
first(n-1); //语句(2)
}
}
/*
递归调用语句(1)时然后递归调用,整个语句(1)
子问题求解完后再来执行语句(2),
一个递归后面的语句,在前面小问题或指问题求解完后才执行,
*/
int main()
{
int n;
scanf("%d",&n);
printf("n=%d\n",n);
first(n);
return 0;
}
相信看完下面的图,递归调用步骤都应该清楚了,如果不清楚,请看下总结,请给我个赞哦;
总结上面问题:一个递归后面的语句,在前面小问题或子问题求解完后才执行,注意问题的镶嵌;
理解递归:是在一个程序过程中,出现直接或间接调用自己的成分,称之为递归(顾名思义,自己调用自己:)
一般地,一个递归模型是由递归出口和递归体两部分组成。所谓递归出口就是,递归到何时结束;递归体是递推关系,
介绍下递归思路,大问题求解-------分解-为-----》若干个相似子问题-----直到-----》每个“小问题”都可以直接解决(此时为递归出口);
注意:但递归分解不是随意的分解,递归分解要 保证“大问题”与“小问题”相似;
经常需要假设的思想
下面通过简单的例子介绍递归结构
例1:求n!
#include <stdio.h>
int nj(int n){
if(n==1)return n; //递归出口
else return n*nj(n-1);//递推关系)递归体
}
int main()
{
int n;
scanf("%d",&n);
printf("%d\n",nj(n));
return 0;
}
例2:现给第1个数组,递推算法求A[0…n-1]中的最小值?
#include<stdio.h>
#include<stdlib.h>
#define N 13
int f(int *A,int n){
if(n==0) return A[0];
if(A[n]>A[n-1])A[n-1]=A[n];
return f(A,n-1);
}
void main()
{
int A[N]={2,3,4,53,3,2,5,6,7,8,4,6,34};
printf("%d",f(A,N-1));
}
例3.采用递归算法正向和反向输出单向链表
算法设计
//正向输出函数
void ZX(DLB*L){
if(L==NULL)return ;
else {
printf("%c",L->data);
ZX(L->next);
}
}
//反向输出函数
void FX(DLB*L){
if(L==NULL)return ;
else {
FX(L->next);
printf("%c",L->data);
}
}
这里发现两个函数就输出位置不同,分析如下图,
整体代码`
#include<stdio.h>
#include<stdlib.h>
typedef struct Listlink{
char data;
struct Listlink* next;
}DLB;
//正向输出函数
void ZX(DLB*L){
if(L==NULL)return ;
else {
printf("%c",L->data);
ZX(L->next);
}
}
//反向输出函数
void FX(DLB*L){
if(L==NULL)return ;
else {
FX(L->next);
printf("%c",L->data);
}
}
void main()
{
char A[]="abcdefg";
int i;
DLB*h,*tail;
h=(DLB*)malloc(sizeof(DLB));
tail=h;
h->next=NULL;
for(i=0;A[i];i++){
DLB*node=(DLB*)malloc(sizeof(DLB));
node->data=A[i];
tail->next=node;
tail=node;
}
tail->next=NULL;
h=h->next;
DLB*L1,*L2;
L1=h;L2=h;
printf("正向输出如下\n");
ZX(L1);
printf("\n反向输出如下\n");
FX(L2);
return 0;
}
例4;迷宫问题 例下面的迷宫用递归算法;求出从(1,1)==到=》(4,4)所有路径, ,1代表可走~0代表不可走位置;起点与终点已加粗加斜如下,,
1,1, 1, 1, 1, 1
1, 0, 0, 0, 1, 1,
1, 0, 1, 0, 0, 1
1, 0, 0, 0, 1 ,1
1, 1, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1
整体代码如下:只需观察递归函数即可;
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 25
#define N 6
int mg[N][N]={{1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 1},
{1, 0, 1, 0, 0, 1},
{1, 0, 0, 0, 1 ,1},
{1, 1, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1} };
int count=0;
int put[N][N];//迷宫输出;
typedef struct
{ int i; // 当前方块的行号
int j; // 当前方块的列号
} Box;
typedef struct
{ Box data[MaxSize];
int length; // 路径长度
} PathType; //
void mgpath(int xi,int yi,int xe,int ye,PathType path)
// 求解路径为:(xi,yi) => (xe,ye)
{
//printf("\t(%d,%d)\n",path.data[xi].i, path.data[yi].j);
int di,k,i,j;
if (xi==xe && yi==ye)
{
path.data[path.length].i = xi;
path.data[path.length].j = yi;
path.length++;
printf(" 迷宫路径%d 如下:\n",++count);
int a,b;
for(a=0;a<N;a++){
for(b=0;b<N;b++)
{put[a][b]=0; }
}
printf("\n");
for (k=0;k<path.length;k++) //用于输出迷宫路径不用管/
{
printf("\t(%d,%d)",path.data[k].i, path.data[k].j);
put[path.data[k].i][path.data[k].j]=1;
if ((k+1)%5==0) // 每输出每5个方块后换一行
printf("\n");
}
printf("\n");
for(a=0;a<N;a++){
for(b=0;b<N;b++)
{printf("%d",put[a][b]); }
printf("\n");
} /
printf("\n");
}
else //(xi,yi) 不是出口
{
if (mg[xi][yi]==0) //(xi,yi) 是一个可走方块
{
di=0;
while (di<4) // 对于(xi,yi) 四周的每一个相邻方位di
{
switch(di) // 找方位di 对应的方块(i,j)
{
case 0:i=xi-1; j=yi; break;
case 1:i=xi; j=yi+1; break;
case 2:i=xi+1; j=yi; break;
case 3:i=xi; j=yi-1; break;
}
path.data[path.length].i = xi;
path.data[path.length].j = yi;
path.length++; // 路径长度增1
mg[xi][yi]=-1; //避免来回重复找路径
mgpath(i,j,xe,ye,path);
path.length--; // 回退一个方块
mg[xi][yi]=0; // 恢复(xi,yi) 为可走
di++;
} //-while
} //- if (mg[xi][yi]==0)
} //- 递归体
}
int main()
{
PathType path;
path.length=0;
mgpath(1,1,4,4,path);
return 0;
}
看不懂代码没关系,接下来我们分析问题分解,
首先,各位小伙伴,可能在递归函数中没有发现return 语句,因为这里递归算法输出所有的路径,
如果出现return语句,输出一条路径就结束了,在上面代码中,结构体语句定义Box(表示步),然后你定义,PathType(路径);
typedef struct
{ int i; // 当前方块的行号
int j; // 当前方块的列号
} Box;
typedef struct
{ Box data[MaxSize];
int length; // 路径长度
} PathType; //
接下来问题分解
大问题(前提是可以走的步数):从(1,1)走到(4,4)--------》走到(1,1)四周的附近一步-----》走到附近一步的一步--------》。。。-------》走到(4,4);
小问题:可走的每一步;
下面分析这个代码
else //(xi,yi) 不是出口
{
if (mg[xi][yi]==0) //(xi,yi) 是一个可走方块
{
di=0;
while (di<4) // 对于(xi,yi) 四周的每一个相邻方位di
{
switch(di) // 找方位di 对应的方块(i,j)
{
case 0:i=xi-1; j=yi; break;
case 1:i=xi; j=yi+1; break;
case 2:i=xi+1; j=yi; break;
case 3:i=xi; j=yi-1; break;
}
path.data[path.length].i = xi;
path.data[path.length].j = yi;
path.length++; // 路径长度增1
mg[xi][yi]=-1; //避免来回重复找路径
mgpath(i,j,xe,ye,path);
path.length--; // 回退一个方块
mg[xi][yi]=0; // 恢复(xi,yi) 为可走
di++;
} //-while
} //- if (mg[xi][yi]==0)
} //- 递归体
}
判断是否可走,如果可走,从相邻方位找四周可走的方块,对每个方块首先,把(xi,yi)存到path数组中;然后把其迷宫值置为-1;避免重复走,定为调用小问题,走一步,接下来就是回退(path.length–)恢复(xi,yi)的迷宫值为0;
例5,汉诺塔问题
三阶问题,经典题目 有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子C上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问至少需要多少次移动,设移动次数为H(n)
first :每次移动同一根柱子上都不能出现大盘子在小盘子上方可能分析过程有点懵逼,,
假设1有一个圆盘a:将a盘从A柱移到C柱
假设2有两个圆盘a,b;(b盘>a盘);首先将a盘从A柱移到B柱–》将b盘从A柱移到C柱-----》将a盘从B柱移到C柱 (共3步)
假设3:有三个圆盘a,b,c(c盘>b盘>a盘);首先将a盘从A柱移到C柱—》将b盘从A柱移到B柱----》将a盘从C柱移到B柱---------》将c盘从A柱移到C柱-》(加粗部分和上面2个盘问题相同)将a盘从B柱移到A柱----》将b盘从B柱移到C柱-------》将a盘从A柱移到C柱;(注:加粗部分和假设2相似 )(共7步)
假设4:有四个圆盘a,b,c,d(d盘>c盘>b盘>a盘 ) :(1)首先将a盘从A柱移到B柱—》将b盘从A柱移到C柱----》将a盘从B柱移到C柱;-------》将c盘从A柱移到B柱—》将a盘从C柱移到A柱-----》将b盘从C柱移到B柱------》将a盘从A柱移到B柱;((1)与假设3一样共7步)----》将d盘从A柱移到C柱。。。。。。。。。。
记:move(n,x,y,z)为将n个盘借助y盘从x盘移动到z盘:则:
假设1:a盘move(1,A,B,C)
假设2:a盘move(1,A,C,B)—>b盘move(1,A,B,C)----->a盘(1,B,A,C)等价于a,b盘move(2,A,B,C)
假设3:a,b盘move(2,A,C,B)–>c盘move(1,A,B,C)–>a,b盘(2,B,A,C)等价于a,b,c盘move(3,A,B,C)
假设4:a,b,c盘move(3,A,C,B)—>d盘move(1,A,B,C)–>a,b,c盘(3,B,A,C)等价于a,b,c,d盘move(4,A,B,C);
。。。假设n:代码如下简单的一逼,完美的体现了数学的简洁美,
#include <stdio.h>
#include<stdlib.h>
#define N 3 //盘数
void hanoi(char*pan,int n,char x,char y,char z){
if(n==1)
printf("盘%c从%c-->%c\n",pan[n-1],x,z);
else{
hanoi(pan,n-1,x,z,y);printf("盘%c从%c-->%c\n",pan[n-1],x,z);
hanoi(pan,n-1,y,x,z);
}
}
int main()
{
char pan[N+1];
scanf("%s",pan);
hanoi(pan,N,'A','B','C');
system("pause");
return 0;
}