这两天一直在看汉诺塔的递归过程,花了点时间把这个程序仔细分析了几遍,在执行过程的分析中遇到了些问题,上网查了查,也没有找到比较满意的答案,所以就硬着头皮自己解决了,我想我遇到的问题也许很多人也会遇到,于是我就把我对汉诺塔递归过程的分析写了出来,希望对一些人有所帮助,当然,开始前还是声名一下,我这是菜鸟文章,自认为是高手的,还请绕道,谢谢。
废话不多说,上代码:
#include<stdio.h>
void move(char x,char y)
{
printf("%c-->%c/n",x,y);
}
void hanoi(int n,char one,char two,char three)
{
if(n==1)
{
move(one,three);
}
else
{
hanoi(n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);
}
}
void main()
{
int m;
printf("请输入盘子数:");
scanf("%d",&m);
printf("%d个盘子的移动步骤为:/n",m);
hanoi(m,'A','B','C');
}
可以看出,代码是由三部分组成:move()函数、hanoi()函数和main()函数,其中move()函数是用来输出表示输出方式的字符串的,hanoi()函数是用来控制盘子移动的(要明白,这里的控制盘子移动不是真的在移动盘子,只是控制盘子输出方案的),main()函数就不多说了。
问题主要集中在hanoi()中,下面我来具体分析:
我以n==3为例
1 void hanoi(int n,char one,char two,char three)
{
2 if(n==1)
{
3 move(one,three);
}
4 else
{
5 hanoi(n-1,one,three,two);
6 move(one,three);
7 hanoi(n-1,two,one,three);
}
}
在main()函数中,hanoi(n,’A’,’B’,’C’)表示的是n个盘子从A柱经过B柱移到C柱,这里我们只分析n==3的情况。记住,这个hanoi里的参数是实参,实参传递到第1行中的形参,此时,one=A,two=B,three=C。然后走第2行,n不等于1,所以执行else中语句,到第5行,又有一个函数调用,这时n变成2,第5行为hanoi(2,’A’,’C’,’B’),这里一定要理解,然后这一个hanoi里的参数成为实参,接着就是所谓的递归,调用自身,所以程序回到第1行,此时,one=A,two=C,three=B,然后和刚才一样,n不等于1,走第5行,这时第5行为hanoi(1,’A’,’B’,’C’),很刚才一样,这里面的参数是实参,就又回到第1行,此时,one=A,two=B,three=C。接下来走第2行是发现n==1,所以执行move(one,three),也就是会输出AàC,这也就是程序输出的第一个步骤。
这之后就是我当初没有理解的地方了,第3行执行完后再执行哪儿呢?注意了,当n==1后,第3行其实相当于第5行hanoi函数的函数体,第3行执行完后就相当于第5行执行完毕,接下来就开始执行第6行,也许有人会说了,执行了if语句里的程序怎么又执行else里的呢?在这里,就需要大家记住这是个递归过程,这里确实开始有点难理解,所以需要大家好好的想想了。
好,接着刚才的,执行第6行,move(one,three),这时,又有个问题出现了,这里move中的one和three分别是什么呢?也许有人会说,刚才第5行的A,B,C传给第1行后one就是A了,three就是C了呀,在这里,请注意了,第5行的one,two,three和第1行的one,two,three是占用的不同的存储空间,只是名字一样罢了,这个调用只是传递值,而不会传递地址,所以第6行move中的one=A,three=B,所以程序的第二个步骤终于出现了:AàB
OK,继续,搞懂了上面的值传递后,接下来第7行中的参数就应该知道是什么了吧,我还是写出来吧,hanoi(1,’C’,’A’,’B’)。同样,这是实参,调用到第1行,就是one=C,two=A,three=B,然后就是第3行,程序的第三个步骤就出来了:CàB。
到这里,又到了不好理解的地方了,以上的步骤主要是完成了当n==1时的情况,而hanoi(n=1)中包含三个部分:一是hanoi(n=2,one,three,two),二是move(one,three),三是hanoi(2,two,one,three);而hanoi(n=2,one,three,two)中又包含{ hanoi(1,one,three,two),move(one,three),hanoi(1,two,one,three)},所以,以上的步骤也就是说完成了大括号中的内容,接下来就要开始执行和它并列的move(one,three),这里一定要好好琢磨琢磨,理清这里面的层次,搞懂为什么要执行move(one,three)。好了,搞懂之后,执行move(one,three),输出程序的第四个步骤:AàC(注意要搞清楚这里的one和three得到的值是从hanoi(n=3,’A’,’B’,’C’)中来的,因为这是和hanoi(n=2,one,three,two)并列的)
因为move中没有包含其他的语句,所以接下来就开始执行最后一个并列的hanoi函数,也就是hanoi(2,two,one,three)(此时,one=A,two=B,three=C)。这个就和先前最开始的是一模一样的,因为n不等于1,所以到第1行hanoi(2,B,A,C),往下到第5行:hanoi(n=1,B,C,A),在到第3行(接下来我就不详细写了),第五个步骤出来:BàA
再到第6行,第六个步骤也就出来了:BàC
然后同样,走第7行(hanoi(n=1,A,B,C)),理所当然的,最后一个步骤出来了:AàC
至此,当n==3时,汉诺塔的整个递归过程就结束了,对于我们初学者,过程是有点复杂,所以每一步我都尽可能写得详细,希望对大家有所帮助。
当初困扰我的主要有两个方面:
1、 对于递归的理解问题,递归中包含的if else等语句很容易打乱我们对于执行顺序的分析,所以一定要搞清递归是个怎样的过程。
2、 要分清楚实参和形参,搞懂值传递的实质,只有这样才能明白one,two,three和A,B,C是怎么对应的。
这两个方面搞清楚了,整个过程也就很容易理解了。
最后,我想说毕竟我才学到函数这一章,很多东西都不知道,所以其中也许会有理解上的错误也说不定,如果有人发现错误,还请指教了,谢谢。
2010年4月16日
马克