求教!汉诺塔问题

桑夏艾 2011-04-21 11:06:49
每本C语言书上有一个经典的递归调用,Hanoi,相信大家都知道,研究了很久也没搞清楚hanoi函数是怎么工作的。
源代码:

#include<stdio.h>

void main()
{
void hanoi(int n,char one,char two,char three);/*hanoi原型声明*/

int m;
printf("input the number of disks:");
scanf("%d",&m);
printf("The step to moving %d diskes:\n",m);

hanoi(m,'A','B','C');

}

void hanoi(int n,char one,char two,char three) /*定义hanoi函数*/
{
void move(char x,char y); /*对move函数的声明*/

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);
}
答案:input the number of diskes:3
The step to moving the 3 diskes:
A-->C
A-->B
C-->B
A-->C
B-->A
B-->C
A-->C
我是这么分析的,main函数中调用hanoi函数,n=3大于1,执行hanoi(n-1),一直执行知道hanoi(1),输出A-->B
然后再执行move(one,three)输出A-->B,在执行hanoi(n-1);
很明显分析有错!请高手指正,给出详细的分析步骤。
...全文
389 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
桑夏艾 2011-09-17
  • 打赏
  • 举报
回复
谢谢,我已经懂了
cao_julians 2011-07-23
  • 打赏
  • 举报
回复
利用一些全局变量,记录关键函数的被调用次数和函数调用链的调用与返回状况。
1.静态变量count,记录move函数的执行次数
2.全局变量depth,记录每个函数被调用时的嵌套深度(进入函数时++,从函数返回前--)
相关技术:
1.printf函数中格式控制符中有一个*,可以与一个参数值对应来控制输出宽度
2.标准宏__func__,是当前函数的名字
3.按函数的嵌套深度对应的位置输出函数的名字及实际参数值,并用=>,<=表示进入一个函数和从一个函数返回
/*Hanoi(河内塔)V3.0*/
#include<stdio.h>
#include<stdlib.h>
int depth=0;
int width=8;/*可调整的输出宽度*/
void move(int sn, char x,char y); /*对move函数的声明*/
void hanoi(int amount,char from,char by,char to);/*hanoi原型声明*/

int main(void) /*ANSI C*/
{

int m; /*the number of disks*/
depth++;
printf("\n%*s=>%s",depth*width," ",__func__);/*进入函数*/
printf("input the number of disks:");
scanf("%d",&m);
printf("The step to moving %d diskes:\n",m);

hanoi(m,'A','B','C');
depth--;
printf("\n%*s<=%s",depth*width," ",__func__);/*从函数返回*/
}

void hanoi(int amount,char from,char by,char to) /*定义hanoi函数*/
{
depth++;
printf("\n%*s=>%s(%d,%c,%c,%c)",depth*width," ",__func__,amount,from,by,to);/**/

if(amount==1) /*数量为1时*/
move(amount,from,to);/* 第1号盘直接从源柱到目标柱*/
else
{
hanoi(amount-1,from,to,by); /*amount-1是数量,从源柱借助目标柱,将1至amount-1号盘移到中间柱*/
move(amount,from,to); /*最大的第amount号disk直接从源柱至目标柱,这里amount是序号*/
hanoi(amount-1,by,from,to); /*从中间柱将amount-1个盘,借助源柱移到目标柱*/
}
depth--;
printf("\n%*s<=%s(%d,%c,%c,%c)",depth*width," ",__func__,amount,from,by,to);/**/


}

void move(int sn,char from,char to)/* sn是盘的序号*/
{
static int count=0;
depth++;
printf("\n%*s=>%s(%d,%c,%c)",depth*width," ",__func__,amount,from,to);/**/


printf("\nmove(%d):disk %d: %c-->%c",++count,sn,from,to);/*增加move函数的调用次数*/
depth--;
printf("\n%*s<=%s(%d,%c,%c)",depth*width," ",__func__,amount,from,to);/**/


}
=============================
在V2.0版代码上直接改写的,输出格式可能不美观。但是会把函数调用的实际嵌套情况准确表示出来的。对于理解递归调用会有作用的。----供参考。

cao_julians 2011-07-22
  • 打赏
  • 举报
回复
Hanoi函数书写有点问题,改一下
void hanoi(int amount,char from,char by,char to) /*定义hanoi函数*/
{

if(amount==1) /*数量为1时*/
move(amount,from,to);/* 第1号盘直接从源柱到目标柱*/
else
{
hanoi(amount-1,from,to,by); /*amount-1是数量,从源柱借助目标柱,将1至amount-1号盘移到中间柱*/
move(amount,from,to); /*最大的第amount号disk直接从源柱至目标柱,这里amount是序号*/
hanoi(amount-1,by,from,to); /*从中间柱将amount-1个盘,借助源柱移到目标柱*/
}
}

cao_julians 2011-07-22
  • 打赏
  • 举报
回复
这些天较忙,LZ已经结帖了。
我借LZ这个帖子,陆续给一些改写版,供大家参考。
LZ转述的称为V1.0版。
#include<stdio.h>

void main()
{
void hanoi(int n,char one,char two,char three);/*hanoi原型声明*/

int m;
printf("input the number of disks:");
scanf("%d",&m);
printf("The step to moving %d diskes:\n",m);

hanoi(m,'A','B','C');

}

void hanoi(int n,char one,char two,char three) /*定义hanoi函数*/
{
void move(char x,char y); /*对move函数的声明*/

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);
}
结构和内容不做大改动,必要的为了提高可读性的改动还是要的,再加上些注释
/*Hanoi(河内塔)V2.0*/
#include<stdio.h>
void move(int sn, char x,char y); /*对move函数的声明*/
void hanoi(int amount,char from,char by,char to);/*hanoi原型声明*/

int main(void) /*ANSI C*/
{

int m; /*the number of disks*/
printf("input the number of disks:");
scanf("%d",&m);
printf("The step to moving %d diskes:\n",m);

hanoi(m,'A','B','C');
/*hanoi(int amount,char from,char by,char to);*/
/*hanoi( m, 'A', 'B', 'C'); */
/*将函数原型和函数调用对照,形参与实参对齐了读:disk的amount数量为m,from从 'A'柱,by借助 'B'柱, to到 'C'柱*/
}

void hanoi(int amount,char from,char by,char to) /*定义hanoi函数*/
{

if(amount==1) /*数量为1时*/
move(amount,from,to);/* 第1号盘直接从源柱到目标柱*/
else
{
hanoi(amount-1,from,to,by); /*amount-1是数量,从源柱借助目标柱,将1至amount-1号盘移到中间柱*/
move(amount,from,to); /*最大的第amount号disk直接从源柱至目标柱,这里amount是序号*/
hanoi(amount-1,by,to,from); /*从中间柱将amount-1个盘,借助目标柱移到源柱*/
/*此时最大的盘已经到达目标柱,不再考虑它了(它不影响其它盘的移动)。而源柱有amount-1个盘,问题与原问题是一致的,但规模小了*/
}
}

void move(int sn,char from,char to)/* sn是盘的序号*/
{
printf("disk %d: %c-->%c\n",sn,from,to);/*连同盘的序号一起输出*/
}
luckyyulin 2011-07-12
  • 打赏
  • 举报
回复
汉诺塔问题真的很有深度啊
carefreedom 2011-07-11
  • 打赏
  • 举报
回复
大象装冰箱,哈哈哈,太有才了。。
vaio008 2011-05-19
  • 打赏
  • 举报
回复
学习了,本人最近刚学到这里,感觉很难,那个大象装冰箱太形象了,果然有很多高手啊
cao_julians 2011-04-25
  • 打赏
  • 举报
回复
关于递归程序设计,我个人以为讲解最好的是Deteil父子写的C程序设计(薛万鹏译,机工社出版)。作为递归程序设计入门,只需抓住两点:
1.一个问题能分成两个子问题(一次就两分),子问题保持了与原问题相同的内涵,但规模小了,这样分法可以不断进行,直至
2.基底问题出现,基底问题即该问题不必再分了,它的解决是一目了然的了
HANIO(其实不必“大国沙文主义”,应该直译为“河内”--越南北方的大城市)塔问题的关键就是分N个盘子为N-1个小盘子和一个大盘子(若分为一个小盘子和下方N-1个大盘子----会发现问题的内涵变了):
将N-1 个小盘子利用目标柱移到空闲柱上(子问题--内涵不变),
将最下方的最后一个大盘子(基底问题)直接移到目标柱上,
将空闲柱上的N-1个盘子利用目标柱(最大盘子在最下方不影响移动)移到原柱上。
现在就可以看出,N-1个盘子在原柱上(问题内涵不变,但规模小了),最大盘子已经到达目标柱子上--可以不再考虑它了。N个盘子问题现在就是N-1个盘子的问题了。
===============================================
谭先生只能就题讲题,大和尚、小和尚。。。倒是HANOI塔 问题形象地讲解了,但是对递归程序设计的思路没有什么实质上的指导帮助。
jasmine 2011-04-25
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 qiyu1988 的回复:]
对于刚学习递归的深表同情,当初这个汉诺塔也折磨了我很久。。。。。。

hanoi(m,'A','B','C');

当 m == 1 时,表示终止条件,这时把 A 移到 C,这一层次的汉诺塔结束
当 m != 1 时,这时情况就和把大象关进冰箱里类似了。
1.把 A 处 m-1 层的砖块移到 B 处, 把冰箱门打开了
2.把 A 处剩下的那个第 m 个砖块移到 C 处, 塞进大象
……
[/Quote]
大开眼界
Inhibitory 2011-04-24
  • 打赏
  • 举报
回复
    public static void main(String[] args) throws Exception {
hanno(5, 'A', 'C', 'B');
}

public static void hanno(int n, char A, char C, char B) {
if (n == 1) {
System.out.printf("%c-->%c\n", A, C);
return;
}

hanno(n - 1, A, B, C);
hanno(1, A, C, B);
hanno(n - 1, B, C, A);
}
Inhibitory 2011-04-24
  • 打赏
  • 举报
回复
1. 老和尚先叫小和尚把上面的N-1个盘子移动到B柱,借助C柱
2. 老和尚把最大的那个盘子移动到C柱
3. 水上和尚再把B柱上的N-1个盘子移动到C柱,借助A柱
qiyu1988 2011-04-23
  • 打赏
  • 举报
回复
对于刚学习递归的深表同情,当初这个汉诺塔也折磨了我很久。。。。。。

hanoi(m,'A','B','C');

当 m == 1 时,表示终止条件,这时把 A 移到 C,这一层次的汉诺塔结束
当 m != 1 时,这时情况就和把大象关进冰箱里类似了。
1.把 A 处 m-1 层的砖块移到 B 处, 把冰箱门打开了
2.把 A 处剩下的那个第 m 个砖块移到 C 处, 塞进大象
3.把 B 处的 m-1 层砖块移到 C 处, 关门了

这里说的移动就是 move() 函数的操作,也就是打印字符
若第3步里面 m-1 的值不是 1 ,就得重复 m != 1 的情况了,这里就是递归的深入点
当然把大象关进冰箱没这么复杂

我觉得汉诺塔是递归里面最简单,而且最能学到递归思想的一个程序,汉诺塔理解后,再去学习下八皇后

希望对你有帮助,god bless you, amen !
书虫 2011-04-23
  • 打赏
  • 举报
回复
wizard_tiger 2011-04-23
  • 打赏
  • 举报
回复
这个递归好像是这样的:

(1) 以柱子3作为中间柱子,把前n-1个盘子从柱子1移动到柱子2。
(2) 把盘子n从柱子1移动到柱子3。
(3) 以柱子1作为中间柱子,把前n-1个盘子从柱子2移动到柱子3。
jixingzhong 2011-04-23
  • 打赏
  • 举报
回复
对于递归,可以减少递归层次来理解。

考虑汉诺塔仅有1层、2层的移动情况,然后逐渐增加.....
桑夏艾 2011-04-23
  • 打赏
  • 举报
回复
恩,要好一点了,不过还是晕晕的,我再慢慢的推推,我相信我一定会理解的!
桑夏艾 2011-04-22
  • 打赏
  • 举报
回复
谢谢你对我的提醒,在网上搜了很多Hanoi的说法,大家都说递归重要的是思想,思想对了,结果一般都是对的。我才刚学C,对递归不太理解。谢谢你的帮助
nossiac 2011-04-21
  • 打赏
  • 举报
回复
我觉得首先你要理解好递归。。。。。递归法是跟问题的实际解决步骤相反,主要是利用了计算机的栈式函数调用能力,内存够大,就可以无限深层调用,直到某个临界条件,再逐层回溯。
某种角度上,递归跟数学上的归纳证明法相通。
如果f(k)成立,一定有f(k+1)成立,那么,只要证明f(1)成立,整个自然数集就都成立了。

所以,hanoi,只需要搞k=1这种情况。其它就可以用递归解决。

// 这个函数表示,完成n个盘子在三个柱子上的hanoi运算。
//我们要“证明” ,hanoi(n-1)如果能搞定,那hanoi(n)也就能搞定!
//然后,只需要给出hanoi(1)的解决方式就OK了。

void hanoi(int n,char one,char two,char three) /*定义hanoi函数*/
{
void move(char x,char y); /*对move函数的声明*/

if(n==1) //当n=1时,问题很简单。
move(one,three);
else //n不等于1时,注意理解递归的妙处。
{
/*下面其实可以看作由f(k)->f(k+1)的一个推理。
假设hanoi(n-1)的我们能搞定,则通过下面的过程,我们能搞定hanoi(n)!!*/
hanoi(n-1,one,three,two); // 我们当A柱最底下那个盘子不存在,把上面N-1个盘子的移动到B上面。
move(one,three); //把刚才没管的那个盘子放到C柱上。
hanoi(n-1,two,one,three);//把B柱上的盘子再移到C柱上。
/* 这时结合前面n=1的情景,可以得出,这个方法对任意的N都适用。 */
}
}


33,311

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 新手乐园
社区管理员
  • 新手乐园社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧