汉诺(Hanoi)塔问题

用C语言解决(Hanoi)汉诺塔问题

首先,我们先给出(Hanoi)汉诺塔问题:

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

然后我们开始分析(Hanoi)汉诺塔问题:

要解决这个问题从上往下推肯定是不好做的(自己可以试试),因为在移盘子时第一个盘子(即最小的盘子)要移的次数太多了而且无法判断最开始移到哪里是最好的方法,这时我们可以从最后一个盘子开始分析,因为最下面的一个盘子移动的次数最少,只需要移动一次,即A到C.

先给盘子编号从上到下1-64。(看推理过程时可以配合着下方的流程图观看)

首先我们先分析64号从AC之前的过程:

在64号被移到C之前,我们先要把1-63号全部移到B上,然后就可以将64号

了。

但要把1-63号移到B上,我们肯定是要把63移到B上的,所以我们就需要把1-62先

移到C上,然后就可以将63移到B上了。

这里可以看出两个步骤很相似但又有区别,这个区别只是B和C的调换。(记住这一点)

然后我们依此类推就可以推出最开始1号是移到C上还是B上(到底是哪个我们先不管)。“这一类似重复部分我称为上循环”

然后我们再分析64号从AC移动之后的过程。

在64号被移到C之后,我们则要把1-63先全部移到C上,因此我们需要把B上的1-62全部移到A上,然后就可以把63号移到C上了。

但要把1-62移到A上,我们肯定是要把62移到A上的,所以我们就又需要把1-61先移到C上(这时C是可以随便用的,因为64号这个盘子的上面谁都可以放)。所以这就又进入了一个推理循环。

然后我们以此类推就可以推出接下来要先把1号移到C上还是B上(是哪个同样不管)“这一类似重复部分我称为下循环”

这样一来,我们就有两部分推理过程了,将其结构呈现在下方:图2.1中

当这个程序流程图完全结束后(这当然不是人能做到的事了),最右边的(假想一下,需要插入每个循环的中间的一次移动)就是最终的步骤了。

而接下来的难题便是如何根据这个推理来编写一个程序把最后的步骤写出来。

首先,我们明显可以看到这是一个循环的过程(其循环的部分图2.2可见),但是用单纯的循环貌似不是解决问题的最好方法,因为要用到循环套循环,而且是多层循环。

而《函数的递归》(详见此链接腾讯文档)就可以很好的解决这个问题。

下面解释如何编写程序(因为是循环套循环,所以可以使用函数的递归,让函数自己调用自己)。

先写出函数头即:void hanoi(int m,char one,char two,char three) m代表盘子数,one,two,three用来代表A,B,C三个底盘和杆。

然后设计函数体:首先既然要用到递归则肯定要先设计停止的位置。而这里则可以知道递归停止的判断标志是盘子的数量(每循环一次就要减一个),当盘子的数量达到最后的一个盘子时则不能再次递归下去。那么当盘子最后只剩一个时,当移动了这个盘子后,我们就要停止递归了。(详情请看《函数的递归》)

那么则有:

If(m==1){将盘子(这是64号盘子)从AC}else{ 执行另外的语句 }

然后我们就要设计另外的语句了,

从上面可以看出这个递归是:循环;(例如:1-63号或者是1-62号)

移动;(例如:AC或者是AB)

循环;(例如:1-63号或者是1-62号)

而循环的就是函数了,即:hanoi();

移动;

hanoi();(不懂得话可以自己利用图2.1的形式将循环部分替换成hanoi())

然后就是移动了,那么如何设计移动呢?

还记得之前特别强调的B和C互换吗?这里就要用到了。(前面提到的“不用管在最开始到底移动到B还是C”也是这个原因,B和C会互换的,有兴趣研究的可以自己对程序改动一下试着编写找出到底最开始应该移动到谁上。)

看这个两个循环,是不是有点相似但又不完全一样呢?

是的,就是十分相似。我们可以看见在上循环(前有解释)中,中间的移动部分是在AC和AB中不断调换的,而这个可以通过改变函数的变量位置,即hanio()里面的字符变量位置来实现。

调换如下:hanoi(i-1,one,three,two);

在这个里面进入函数时函数里的B和C的位置就调换了,解释如下:

void hanoi(int i,char one,char two,char three){

      hanoi(i-1,one,three,two);

      move(one,three);

      hanoi(i-1,two,one,three);

}

在这个1函数中最开始one=A,two=B,three=C,而进入hanoi(i-1,one,three,two);后,在这个2函数体里的one=A,two=C,three=B。(因为函数的变量是自动变量,函数变量被附的值到底是什么是按在引用函数时在函数里的放置实参的位置来的,所以1函数的实参被赋给2函数的形参,即:在hanoi(i-1,one,three,two);中其实是hanoi(i-1,A,C,B)所以函数体里面的变量two=C,three=B。)(不懂的请观看《函数的调用》)

下循环依此类推就可以得到为什么是hanoi(i-1,two,one,three);这个样子的了。

那么到此为止,函数体就设计好了,然后再完善一下就好了。

最后完善之后就可以得到如下函数:(move函数较简单,这里不做介绍)

(Hanoi)汉诺塔的步骤和步数(C语言代码):

#include<stdio.h>

//外部变量 
int a=0;

//主函数 
int main(){

    void hanoi(int i,char one,char two,char three);
    
    int n;
    
    printf("请输入盘子数:");
    scanf("%d",&n);
    
    hanoi(n,'A','B','C');
    
    printf("共需要%d步",a);
    
    return 0;
}

//递归加嵌套函数 
void hanoi(int i,char one,char two,char three){
    void move(int x,int y);
    
    if(i==1){
        move(one,three);
    }
    else{
        hanoi(i-1,one,three,two);
        move(one,three);
        hanoi(i-1,two,one,three);       
    }
}

//步骤和步数 
void move(int x,int y){
    a++;
    printf("%c-->%c\n",x,y);

}

以上代码是根据谭浩强教授的《C程序设计(第五版)》改编而来!

  • 10
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值