之前遇上的递归问题都比较简单,至少都有一个明确的切入点,例如随机生成迷宫、扫雷等,通常都是从一点开始向外扩展,那一点就是切入点。
直到碰上汉诺塔问题,两眼一抹黑,没有思路。还是靠看别人的思路才解决的。
打算以后碰上新的递归问题,都把思路写一写。
1、汉诺塔
汉诺塔问题概述就不写了
假设共有n个圆盘在A柱(圆盘当前所在柱)上,B(中间柱)、C(目标柱)柱上暂时空空如也,想要把n个圆盘全部移到C柱上。
由于汉诺塔问题的限制导致势必有那么一个时刻,前n-1个圆盘从A柱转移到了B柱上,此时n号圆盘才能离开A柱到达C柱,对于留在B柱上的n-1个圆盘最终也要转移到C柱。
于是问题分为三步:1、前n-1个圆盘从A柱转移到B柱
2、n号圆盘从A柱转移到C柱
3、前n-1个圆盘从B柱转移到C柱
2好说,主要问题是1、3。
1、前n-1个圆盘从A柱转移到B柱
由于第n个圆盘比其他所有盘子都大,对于圆盘的转移没有任何影响,所以这个问题与汉诺塔本身相比除了圆盘少了一个(n到n-1),终点柱子变了(C柱变B柱)以外,没有任何区别,甚至可以看做是另一个汉诺塔问题。
那么按照之前的思路有:1.1、前n-2个圆盘从A柱转移到C柱
2.2、n-1号圆盘从A柱转移到B柱
2.3、前n-1个圆盘从C柱转移到B柱
3、问题也是同理有:3.1、前n-2个圆盘从B柱转移到A柱
3.2、n-1号圆盘从B柱转移到C柱
3.3、前n-1个圆盘从A柱转移到C柱
既然问题分支的解决方法与问题本身几乎一致,那就可以考虑下递归能否解决了。
如果现在搭一个递归的架子,那大概是这样:
void hanion(int n ,xxxxxxxx)//n个圆盘的转移 { hanion(--n,xxxxxxx);//n-1个圆盘的转移 printf("第%d个圆盘从xxxx柱---->xxxx柱",n,xxxxx,xxxxx);//n号圆盘的转移 hanion(--n,xxxxxxx);//n-1个圆盘的转移 }
一个大概的样子有了,那怎么让这个函数知道这些圆盘当前在哪根柱子上,接下来又要去那根柱子上呢?
我们观察1、2、3 以及 1对应的1.1、1.2、1.3 还有3对应的3.1、3.2、3.3 可以发现
任何一个x个圆盘的转移问题,如果按上述思路分为三步,那么其:
圆盘当前所在柱(A) 会成为 第1、2步的 圆盘当前所在柱
中间柱(B) 会成为第1步的 目标柱 和第3步的 圆盘当前所在柱
目标柱(C) 会成为 第1、2步的 目标柱
不管函数嵌套到何种程度,这个关系都不会改变,只要将这个关系写入函数声明,在后续的递归执行中就能正确表示圆盘的位置与转移关系了。
可以定义三个形参a,b,c,用它们代表圆盘当前所在柱、中间柱、目标柱。
void hanion(int n ,char a,char b,char c)//n个圆盘的转移
那么按照之前得到的规律,函数定义会变成:
void hanion(int n ,char a,char b,char c)//n个圆盘的转移 { hanion(--n,a,c,b);//n-1个圆盘的转移 printf("第%d个圆盘从%c柱---->%c柱",n,a,c);//n号圆盘的转移 hanion(--n,b,a,c);//n-1个圆盘的转移 }
现在函数还缺一个可靠的出口,显然光靠一个return 并不行。
针对n,容易想到这玩意儿小于1就没有意义了,所以要加上:
if(n==1) return;
它需要加在当前所写的函数定义的开头位置,否则无法阻止递归的进行,但要注意既然写在printf语句前,那么这段程序就不会执行了,而它想输出的是 “第1个圆盘从xxx柱--->xxx柱”,这是有用的。
所以要在if语句中加上 printf("第1个圆盘从%c柱--->%c柱",a,c); 或printf("第%d个圆盘从%c柱--->%c柱",n,a,c);(此时n就是1) ,改为:
void hanion(int n ,char a,char b,char c)//n个圆盘的转移 { if(n==1) { printf("第1个圆盘从%c柱--->%c柱",a,c); return 0; } hanion(--n,a,c,b);//n-1个圆盘的转移 printf("第%d个圆盘从%c柱--->%c柱",n,a,c);//n号圆盘的转移 hanion(--n,b,a,c);//n-1个圆盘的转移 }
原文:https://www.cnblogs.com/skyrim-Dragonborn/p/13325253.html