to初学者:从汉诺塔问题深入理解递归算法思想
计算机专业学习编程语言学到递归时会举一个汉诺塔问题的经典例子:有A,B,C三根柱子,A柱上按大小顺序从下往上摞着n片圆盘,现在要将这些圆盘从A柱移至C柱,并保持上小下大的顺序。移动规则如下:1、每次只能移动一个盘。2、大盘不能放在小盘上。
用非递归方法要一大串代码,而递归方法就非常简短。递归算法C语言代码如下:
#include <stdio.h> void hanoi(int dishs,char p1,char p2,char p3) { if(dishs==1){ printf("盘子从%c移动到%c\n",p1,p3); { else{ hanoi(dishs-1,p1,p3,p2); printf("盘子从%c移动到%c\n",p1,p3); hanoi(dishs-1,p2,p1,p3); } } void main() { int n; printf("输入盘子的个数:\n"); scanf("%d",&n); hanoi(n,'A','B','C'); }
算法思路是:先将第n个盘上面的n-1个盘移至B柱,再把最底的第n个盘移至C柱,然后把B柱上的n-1个盘移至C驻。问题来了,不是说每次只能移动一个盘吗?怎么能把n-1个盘整体移动呢?
要说明这个问题,先看看一个更简单的例子,等差数列问题:一个等差数列第一项是100,每项差值为3,即f(0)=100,f(n)=f(n-1)+3,求第n个数。用递归方法可以这样写:
int f(n) { if(n==1) return 100; else return f(n-1) + 3; } void main() { int n,r; printf("输入要查询的第n个数:\n"); scanf("%d",&n); r=f(n); printf(r); }
算法思路是:要求第n个数,必须先求出第n-1个数,要求第n-1个数,必须先求第n-2个数,直至第1个数可以直接求出,然后不断反推求出第n个数。这就是递归的的思想,先传‘递’出去,再回‘归’到本位。写成剧本是这样的,第n项对n-1项说:老板要查询我的值,你告诉我你的值,我加上3就行了。n-1项说好啊,不过我也不知道我的值,我要再问问我的前项,它给我后我加上3就是我的值,我到时再给你。这个对话一直传下去,直到首项的小弟弟告诉第2项的兄弟:我的值是100。这样问题就全解决了。
现在再来看汉诺塔问题就容易理解了,剧本是这样的,n层的盘子对上面的盘子说:你们全部挪到B柱,我再挪到C柱,然后你们再过来不就行了么。n-1层的盘子说:你说的简单,每次只能挪1个盘呀,我们怎样全部挪到B柱?n层盘子说:笨蛋,反正我最大,你们尽管放在我上面木有问题,你们完全可以无视我的存在。这样你们n-1个盘全部挪到B柱,与我们n个盘全部挪到C柱所面临的问题不是一样的吗?都是移柱的问题呀,你们还比我少了一个盘,问题复杂度还简化了呢,你就照样画葫芦就行了。n-1层的盘说:哇塞!说的对!上面n-2个盘听好了,我们现在要全部挪到B柱,你们n-2个盘先全部挪到C柱,我再挪到B柱,然后你们再过来。这个对话一直往上传直到最顶层,问题简化为1个盘从A柱挪到C柱。解决了这个问题,其它问题也就全解决了。
总结:如果一个问题有层次自相似性,那么就可以用递归方法,不断回溯简化问题,直至找到初值然后倒推出结论。