c语言中的汉诺塔问题详解

汉诺塔问题是一个古典的数学问题,也是c语言学习中一个用递归方法解题的典型实例,我们先看一下原题。

相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。 

对于这样一个问题,我们第一印象就是十分的复杂,写出移动盘子的每一步似乎非常的困难,但我们可以利用下面的递归方法来解决。

首先分析,将n个盘子从A移动到C可以分解为三个步骤

(1)将A上的n-1个盘子借助C移动到B上;

(2)将A上剩下的一个盘子移到C上;

(3)将n-1个盘子从B移动到C上;

这对于写代码来说就变得十分简单了,但是依然很抽象,不过不要急,我们先把代码写出来再慢慢分析,

这是代码    其中hanoi函数有四个值,hano(n,A,B,C),代表着我们将盘子从A借助B移动到C上

#include <stdio.h>
void hanoi(int n,char X,char Y,char Z)//n个盘子在A上,借助B,移动到C上
{
  if(n==1)//如果A上只有一个盘子,直接移动到C上
	printf("%c-->%c\n",X,Z);
  else//一旦多余一个,则执行递归程序
  {
	hanoi(n-1,X,Z,Y);//将A上的n-1个盘子,借助C,移动到B上
    printf("%c--> %c\n",X,Z);//将A 上的最后一个盘子移动到C上
    hanoi(n-1,Y,X,Z);//将B上的n-1个盘子,借助A,移动到C上
  }
}
 
int main()
{
	int n;
	printf("请输入盘子的总数:");
	scanf("%d",&n);
	hanoi(n,'A','B','C');
	return 0;
}

我第一次看到这里时,就被震撼到了,一个如此复杂的数学问题竟然可以通过短短二十几行代码来实现,可是里面对于hanio函数的反复调用却让我十分头疼,这是怎么实现的呢?

下面我们以n=3为例来详细的解释一遍,这里我们需要用到一点的概念,简单说就是每一次递归之前程序都会将现在的数据储存在栈中(从下往上存储),递归结束后会依次从上往下调出数据,之后程序就会返回到这个数据存储的地方向后继续执行,此时栈中的这一条数据也会消失。

当程序执行到第一个hanoi函数时,显然n!=1,执行else的部分,此时hanoi函数中的变量值为hanoi(3,A,B,C),在执行第8行语句之前,首先要将其存放到栈中。经过下一行的递归后,四个值变为hanoi(2,A,C,B)。

此时栈中的数据为

第一次   hanoi(2,A,C,B)
初始  hanoi(3,A,B,C)

再继续第二次运行else后的代码,得到下一组数值hanoi(1,A,B,C)并储存, 

再运行时n=1,终于可以执行printf语句啦!

输出    A-->C

这时程序就要开始处理栈中的数据了,从上往下执行,将程序返回原处,赋值后执行下一行,此时函数值为hanoi(2,A,C ,B)

输出 A-->B 

继续执行时又遇到了递归程序,这时再将此时的数据储存到栈中,重新开始执行一个hanoi函数,此时hanoi(1,C,A,B)n=1,那么执行if后的语句

输出C-->B

之后返回栈中的第一条数据处,往下执行,这个时候我们发现,第一次递归的hanoi函数到此结束,程序接着处理栈中最底下的数据,hanoi(3,A,B,C)。将程序返回到这里并执行下面的语句

 输出  A-->C

下面一条又是递归啦,我们再重复上面的过程,储存数据再重新调用hanoi函数。调用后hanoi(2,B,A,C),依旧不满足n=1,再重复以上过程,得到hanoi(1,B,C,A)

此时栈中的数据为

hanoi(2,B,A,C)
hanoi(3,A,B,C)

现在n=1,输出B-->A

返回栈中第一条数据处,往后执行

输出B-->C

递归得hanoi(1,A,B,C)

再输出A-->C

此时栈中的数据为

hanoi(2,B,A,C)
hanoi(3,A,B,C)

分别对应的两个数据位置都位于hanoi函数的这个位置

在返回时,都会结束程序,所以整个递归到此结束,让我们统计一下输出的结果

输出 A-->C

输出 A-->B 

输出C-->B

输出A-->C

输出B-->A

输出B-->C

输出A-->C

 

这就是n=3时,整个程序的运行的结果了。

总结:汉诺塔程序本身是一个极其复杂的问题,通过递归,我们可以节省大量的代码,通过一个简短的程序来解决这个问题,但是这其中的逻辑是不可能被简化的,只是由计算机替我们执行了而已,所以彻底的了解整个程序是如何对于hanoi函数一次次的调用,虽然有一定的难度,有点难以理解,我认为是非常有必要的,这可以让我们对递归运算有一个更深刻的了解。以上是n=3的情况,较为简单,大家有能力的可以挑战一下给n赋更大的值,那样递归分析的难度也会同样暴增。

另外,如果想要统计移动次数的话,还可以增加一个全局变量来达到目的。

下面是代码

#include <stdio.h>
int i=0;//增加一个全局变量
void hanoi(int n,char X,char Y,char Z)             //n个盘子在A上,借助B,移动到C上
{
	i++;                  //每次调用hanoi函数,i都会+1
  if(n==1)                                       //如果A上只有一个盘子,直接移动到C上
	printf("%c-->%c\n",X,Z);
  else                                        //一旦多余一个,则执行递归程序
  {
	hanoi(n-1,X,Z,Y);                            //将A上的n-1个盘子,借助C,移动到B上
    printf("%c--> %c\n",X,Z);                        //将A 上的最后一个盘子移动到C上
    hanoi(n-1,Y,X,Z);                          //将B上的n-1个盘子,借助A,移动到C上
  }
}
 
int main()
{
	int n;
	printf("请输入盘子的总数:");
	scanf("%d",&n);
	hanoi(n,'A','B','C');
	printf("一共需要移动的次数为%d",i);
	return 0;
}

运行截图

 

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值