1、定义
函数的递归调用是指在调用一个函数的过程中又出现直接或间接地调用该函数本身,成为函数地递归调用。
2、简单案例(目的是便于理解,该调用是错误的调用,是个无限循环)
int a(int x)
{
int y,z;
z=a(y);
return (z);
}
3、汉诺塔问题
古代有一个梵塔,塔内有3个座位A,B,C。开始时A座上有64个盘子,盘子大小不等,大的在下,小的在上。有一个老和尚想把64个盘子从A座移动到C座,但规定每次只允许移动一个盘,且在移动过程中,在3个座上都始终大盘在下,小盘在上。在移动过程中可以利用B座。问编程怎么写?
解题思路:
第一步
(1)找一个和尚2,将63个盘子从A座移动到B座。
(2)老和尚自己将1个盘子(最底下的盘子)从A移动到C。
(3)再命令找到的和尚2将63个盘子从B座移动到C座。
至此,老和尚的任务完成,但是出现了新的问题,和尚2如何完成将63个盘子从A到B的呢?为此出现了第二步。
第二步
(1)和尚2找来和尚3,将62个盘子从A移到C;
(2)自己将1个盘子从A移到B
(3)和尚2命令和尚3将62个盘子从C移动B;
至此,和尚2的任务完成,但是和尚3是如何完成将62个盘子从A移到C的呢,为此就需要再进行一次递归。
以此类推,最后需要找到第63个和尚将最后两个盘子移动,和尚63就需要找和尚64将倒数第二个大的盘子(也就是从上往下数第一个)移开,和尚63将最大的盘子从一座移动到另外一座,到此工作全部结束。
那么接下来就是和尚64移动了之后,和尚63才能完成任务,和尚63完成了任务,和尚62才可以完成任务,那么老和尚将64个移动完成需要和尚2-和尚64都完成任务才可以。
第三步
为了使得问题简单点,我们先从3个盘子开始。按照上述的情况,我们也分为三步:
(1)将A座的两个盘子移动到B座上。
(2)将A做的一个盘子移动到C座上。
(3)将B座上两个盘子移动到C座上。
图片解释如下:
第四步
第三步的第(1)步又回到了上述的递归问题可以拆分为:
(1)将A座上的一个盘子从A-C
(2)将A座上的一个盘子从A-B
(3)将C座上的一个盘子从C-B
第三步的第(3)步同样也可以分解为:
(1)将B座的一个盘子从B-A
(2)将B做的一个盘子从B-C
(3)将A座的盘子从A-C
以上综合移动三个盘子的步骤为:
A→C,A→B,C→B,A→C,B→A,B→C,A→C
共经历7步,可以推出移动n个盘子的步数为2的n次方减一
那么将3个盘子移动是这样的,将n个盘子从座移动到C座也可以分解为三个步骤:
(1)将A座上n-1个盘子借助C移动到B
(2)将A座上最后一个盘子移动到C
(3)将B座上的n-1个盘子借助A移动到C
将上述三个步骤分为两类操作:
(1)将n-1个盘从一个座移动到另一个座,是小和尚的工作,这是个递归的过程
(2)将1一个盘子从一个座上移动到另外一个座上,这是大和尚的工作
编写程序
分别用两个函数实现上述两类操作,用函数hanoi实现小和尚的任务,用函数move实现大和尚的任务,函数hanoi代表n个盘子从A-C的过程,借助B座,move(x,y)表示将一个盘子从x座移动到y座,x、y、z代表ABC其中之一,具体程序代码如下:
#include <stdio.h>
#include <stdlib.h>
int main(){
//测试递归实验-汉诺塔
void hanoi(int n,char one,char two, char three); //a函数在下面所以需要先声明
int m;
printf("请输入盘子个数:");
scanf("%d",&m);
printf("盘子移动的步骤:\n");
hanoi(m,'A','B','C'); //调用a函数
system("pause");
return 0;
}
void hanoi(int n,char one,char two,char three)
{
void move(char x,char y);//函数声明
if(n==1){
move(one,three);
}else{
hanoi(n-1,one,three,two); //
move(one,three);
hanoi(n-1,two,one,three);
}
}
void move(char x,char y)
{
printf("%c-->%c\n",x,y);
}
运行结果: