(优秀汉诺塔算法)对汉诺塔经典递归问题的理解与讲解(部分引用大神代码,附链接。)

来源:互联网 发布:ubuntu卡在登陆界面 编辑:程序博客网 时间:2024/05/13 18:26

博客大神的优秀汉诺塔代码:喜欢特别冷的冬天下着雪   (侵权联系)

本文只是在大神思路的基础上加以理解。

[cpp] view plain copy
 print?
  1. #include <stdio.h>  
  2. //第一个塔为初始塔,中间的塔为借用塔,最后一个塔为目标塔  
  3. int i=1;//记录步数  
  4. void move(int n,char from,char to) //将编号为n的盘子由from移动到to  
  5. {printf("第%d步:将%d号盘子%c---->%c\n",i++,n,from,to);  
  6. }  
  7. void hanoi(int n,char from,char denpend_on,char to)//将n个盘子由初始塔移动到目标塔(利用借用塔)  
  8. {  
  9.     if (n==1)  
  10.     move(1,from,to);//只有一个盘子是直接将初塔上的盘子移动到目的地  
  11.     else  
  12.     {  
  13.       hanoi(n-1,from,to,denpend_on);//先将初始塔的前n-1个盘子借助目的塔移动到借用塔上  
  14.       move(n,from,to);              //将剩下的一个盘子移动到目的塔上  
  15.       hanoi(n-1,denpend_on,from,to);//最后将借用塔上的n-1个盘子移动到目的塔上  
  16.     }  
  17. }  
  18. void main()  
  19. {  
  20.      printf("请输入盘子的个数:\n");  
  21.      int n;  
  22.      scanf("%d",&n);  
  23.      char x='A',y='B',z='C';  
  24.      printf("盘子移动情况如下:\n");  
  25.      hanoi(n,x,y,z);  
  26. }  

大神的代码很简明,即涉及到了计数,又完美的凸显了整个过程细节,但是对于部分人快速理解来说有些困难。

在递归的过程中,重点在于对第n步与第n-1步之间的关系分析,最基本的就是数学关系:例如f(n)=f(n-1)+f(n-2)、f(n)=f(n-1)*m;

确定了准确的递归关系之后,就需要我们根据程序功能来设定基本值了:例如Fibonacci  就可设置 f(1)=0,f(2)=1;再使用if  等语句来判断条件的达到,从而终止递归,返回上层函数,最后返回答案。

递归问题中,在具体的两步之间研究出其中的规律就能递归下去

在汉诺塔中,基本的数学关系特别简单,就是将第n个(此处假设越往下盘子号数越大)盘子放到目的塔之前,要将上面的n-1个盘子先放到借用塔(步骤1)上,将第n个盘子放到目的塔上(步骤2)后,再将这n-1个盘子从借用塔放到目的塔(步骤3)

当移动次数用函数fun(n)表示时  其中,数量关系是:

步骤1与步骤3移动次数其实是一样的,因为都是移动n-1个盘子到另外一个塔上。

而步骤2只需要一次移动;

所以:f(n)=2*f(n-1)+1;

但是,如果需要明确的看清每一次移动是怎么做到的就必须输出每一步的移动过程,这不是知道简单的数学关系就能解决的,

这里就需要用到,汉诺塔问题递归中存在的另外一种关系:

三座塔:初始塔。目的塔。借用塔的身份转换关系:

具体到f(n)初始塔 是 A   目的塔是C  借用塔是B

对于步骤1:进入到f(n-1)  此时的初始塔依旧是A  但  目的塔变为 B 借用塔变为 C  (不太清楚的话  画画图)

因此这里的递归变化在于   目的塔与借用塔身份互换。

对于步骤2:不需要借用塔,  原来的目的塔与初始塔也无变化。 

对于步骤3:进入到f(n-1) ' 此时的初始塔变为B  但  目的塔变为C 借用塔变为 A (不太清楚的话  画画图)

因此这里的递归变化在于  上面所述的变化。

对于之后的每一次简化递归 上层函数对他下面的函数来说  都是这种塔的身份变换。

因此,我们可以利用这种塔的变换来进行输出中的调用,将三个塔柱命名,通过传参,将每一步正确的移动细节输出。

  1. void move(int n,char from,char to) //将编号为n的盘子由from移动到to  
  2. {printf("第%d步:将%d号盘子%c---->%c\n",i++,n,from,to);  
  3. }  
并且  通过i++来记录一共需要多少步。

去掉那层输出后,就可以看做单纯的计数程序:

#include <stdio.h>  
//第一个塔为初始塔,中间的塔为借用塔,最后一个塔为目标塔  
int i=1;//记录步数  
void move(int n) //将编号为n的盘子由from移动到to  
{printf("第%d步",i++);  
}  
void hanoi(int n)//将n个盘子由初始塔移动到目标塔(利用借用塔)  
{  
    if (n==1)  
    move(1);//只有一个盘子是直接将初塔上的盘子移动到目的地  
    else  
    {  
      hanoi(n-1);//先将初始塔的前n-1个盘子借助目的塔移动到借用塔上  
      move(n);              //将剩下的一个盘子移动到目的塔上  
      hanoi(n-1);//最后将借用塔上的n-1个盘子移动到目的塔上  
    }  
}  
int main()  
{  
     printf("请输入盘子的个数:\n");  
     int n;  
     scanf("%d",&n);  
     char x='A',y='B',z='C';  
     printf("盘子移动情况如下:\n");  
     hanoi(n);  
     return 0;
}  




0 0
原创粉丝点击