最近学习递归的时候看到了这样一个问题,叫做汉诺塔问题,是一个很经典的递归问题,下面是它的原题目的描述:
/****这个问题来源于印度。有三个金刚石塔,第一个从小到大摞着64片黄金圆盘。现在把圆盘按大小顺序重新摆放在最后一个塔上。并且规定,在小圆盘上不能放大圆盘,在三个塔之间一次只能移动一个圆盘。***/
为了能够直观地看看这个问题,这里我们画一个图
我们初始放盘子的柱子A叫做初始柱,C为我们的目标柱,B为我们的中转柱,我们要借助B柱把A上的盘子转移到C柱上
我们要实现这样的过程,我们要确保我们第一个放在C柱上的盘子应该是最大的那个,我们总共有n个盘子,那么我们就要先把最大的盘子上面的n-1个盘子转移到B柱(中转柱)上
我们就把刚刚的问题转化成了细分的小问题,就是先把n-1个上方的小盘子放在B柱上,然后把最下面的最大的盘子放在C柱上(目标柱),再把剩下的n-1个小盘子放在C柱上
即:此次目标:C柱
1.上方n-1盘从A——>B
2.下方的最大n盘从A——>C
3.刚刚放在B柱上的n-1盘从B——>C
——————————————————分割线——————————————————————
像这样再细分,把(n-1)个小盘子分成这部分盘子最下面的那个盘子和上面的n-2个盘子,那么我们就可以继续刚刚的操作这次我们上面的n-1盘的目标柱是B柱
即:此次目标:B柱
1.上方n-2盘从A———>C
2.下方第n-1盘从A———>B
3.把刚刚放在C柱上的n-2个盘放回B柱
这里就是我们对上方的n-1盘的移动处理
当然,我们的套娃过程还没有结束,我们还要用别的方式将这部分的上方n-3的部分再次进行移动
——————————————————分割线—————————————————————
总而言之,不管n是多少,我们执行的过程都是一个模版的,那么我们如果要使用递归实现代码,我们就要设置停止递归的条件
什么时候递归停止套娃呢?我们可以发现,我们将一摞盘子移动到目标柱子上,需要借助中转柱的原因是我们最大的那个盘子上面还有小盘子,所以我们要使用中转柱执行把小盘放在一边然后移动下面的盘子的动作
如果我们只有一个盘子呢?很明显,这个时候我们就可以忽略这个中转柱,从而直接把我们的盘子从起始柱直接放到目标柱上。
那么我们套娃的停止条件就是n=1
———————————————————分割线—————————————————————
需要用代码生动形象表示出这个过程,我们就可以这样做:
定义一个print函数,用来表示把x柱子最上方的盘子放在y柱上,即:
void print(char x,char y)
{
printf("%c-->%c\n",x,y);//表示把x柱子最上方的盘子放在y柱上
}
设置一个move函数参数有 :盘子的数量n,起始柱的编号,中转柱的编号,目标柱的编号
再引入我们刚刚的分析
即:此次目标:C柱
1.上方n-1盘从A——>B
2.下方的最大n盘从A——>C
3.刚刚放在B柱上的n-1盘从B——>C
然后我们就可以写出函数:
void move(int n, char start, char temp, char end)// 参数分别为
// 圆盘初始数量; 初始杆; 中转杆; 目标杆
{
if(n==1)
print(start,end);//如果只有一个圆盘就可以直接把它放在目标杆上
else
{
move(n-1,start,end,temp);//如果要移动最下面的圆盘就要把上面的圆盘移动到中转杆上
print(start,end);//把最大的圆盘移动到目标杆上
move(n-1,temp,start,end);//把刚刚中转盘上面的圆盘都移到目标盘上
}
}
这样我们就完成了对这个题目的解读
———————————————————————————————————————————
那么,我们除了可以进行盘子路径的演示,我们是不是还可以对我们移动盘子的次数进行计算呢?
那么我们就可以定义一个函数movecount,我们不需要对路径研究,我们就只要传入盘子的个数n,下面我们就要进行规律的探寻:
首先我们要知道我们的n和我们移动的次数之间有什么关系,这个时候我们不好思考的话,我们就可以一个一个罗列出来
盘子个数 移动次数
1 1
2 3
3 7
4 15
5 31
这样我们就可以发现除了第一次是1以外,剩下的每一次移动次数都会等于它们盘子减一之后乘2再加上一的值。用movecount表示的话就是,movecount(n)=2*movecount(n-1)- 1
那么我们就可以使用这个函数关系进行递归计算,当然也可以用其他方法,这里就不一一举例了
void movecout(int n)
{
int ret;//定义返回值
if(n==1) ret = 1;//如果为一则只进行一次移动,即n=1为函数的终止条件
else
ret = movecount(n)=2*movecount(n-1)- 1;//如果大于1就进行函数关系式的递推
return ret;//返回值
}
这样我们就完成了汉诺塔问题的整体思路,快去试试吧!