关于Hanoi汉诺塔算法的理解

一、数学归纳法

证明等差数列求和公式

1+2+3+...+n={n(n+1) \over2}

第一步,n=1时,显然左边等于右边

第二步,证明假设n=m时成立,则n=m+1时也成立步骤如下: 

假设n=m时公式成立,即1+2+3+...+m={m(m+1) \over2}......................................(1式)

1式两边加上m+1得到:1+2+3+...+m+m+1={m(m+1) \over2}+m+1    .....(2式)

2式右边整理:{m(m+1) \over2}+m+1={m(m+1) \over2}+{​{2(m+1)}\over 2} ={(m+1)(m+1+1) \over 2}

整理之后的2式:1+2+3+...+m+m+1={(m+1)(m+1+1) \over 2}..................(3式)

观察和1式和3式具有相同的结构,在证明过程中用假设的式子也就是当n=m时的情推导出了当n=m+1时的情况

结论:当n取任何自然数该公式都成立

在这里把"m"替换成"m-1",把"m+1"替换成"m"也成立

也就是说,当n=m-1时成立那么当n=m时也必定成立

先记住这个概念,后面有助于理解递归

 二、递归

递归可以简单理解为函数调用函数本身的一种方法。

举例——计算n的阶乘

int cal(int n )
{
    if(n>1)
        n=n*cal(n-1) ;
    else
        return n ;
}
int main()
{
    int n,result ;
    printf("请输入n的值:\n") ;
    scanf("%d",&n) ;
    printf("result=%d",cal(n)) ;
}

输入n=5时

递——result = 5*cal(4)

           result = 5*4*cal(3)

           result = 5*4*3cal(2)

           result = 5*4*3*2cal(1)

归——result = 5*4*3*2

           result = 5*4*6

           result = 5*24

           result = 120

在代码段中的cal函数 调用自身的时候层层往下,直到n =1时返回1,函数的结果才能够一层一层的往回返回结果,于是就有了“归”

 三、递归函数在调用自身时传参的变化

#include <stdio.h>
int count = 0 ;
void   test(char a ,char b ,char c )
{
    printf("%c,%5c,%5c\n",a,b,c) ;
    count++ ;
    if(count== 5)
        return  ;
    test(b,c,a) ;
}
int main()
{
    test('A','B','C') ;
}

在main函数中,将'A','B','C'三个实参分别传递给test函数中a,b,c

在test函数中又将b,c,a 传递给自身test函数的a,b,c

在主函数传递给test函数时a,b,c对应的是'A','B','C'

在test函数第一次传递给自身的时候,是把'B','C','A'分别传给a,b,c

也就是说在test函数第一次调用自己后a,b,c由本来的'A','B','C'变成了’B','C','A'

代码运行5次后结果如下:

四、理解汉诺塔算法

递归问题用递归的想法来思考会简单很多,可以省去很多不必要的麻烦,如果展开来讲属实是自找苦吃,但是汉诺塔算法这个递归却又不像计算阶乘一样很难想象它的步骤是如何进行的,于是我便写下了这篇文章,试着从代码的角度去理解。

先讲一下递归的角度是怎么解决的。

当只有一个盘子的时候:显然一步由A到C就解决了

当只有两个盘子的时候:

当有n个盘子(n>2)的时候:用递归的思想来说就是把A针最上面的n-1个盘子看作一个盘子然后重复只有两个盘子的情况的步骤,其中最终要的就是要不停的把n-1个盘子不停的细分也就是'递',直到细分成一个盘子的时候就可以'归'。代码如下:

#include <stdio.h>
void printmove(int x,int y)
{
    printf("%c----->%c\n",x,y) ;
}
void hanoi(int n,char a ,char b,char c)
{
    if(n==1)
        printmove(a,c); //移动圆盘从第一根针到第三根针
    else{
        hanoi(n-1,a,c,b) ;
        printmove(a,c) ;
        hanoi(n-1,b,a,c) ;
    }
}
int main()
{
    int n ;
    printf("请输入盘子数:")  ;
    scanf("%d",&n) ;
    hanoi(n,'A','B','C') ;
}

前面讲过递归函数在调用自身时传参的变化

在上面代码中hanoi(n-1,a,c,b) ;和hanoi(n-1,b,a,c) ;实际上就是b针和c针的互换以及a针与b针位置的互换,还有就是printmove(a,c);意思是将第一根针的圆盘移动到第三根针,不一定就是A---->C。目的就是为了保证每次调用hanoi函数时,三根针上的圆盘的摆放位置都是以及圆盘的移动方法都跟n=2时的情况一样

互换前:

  
上面的图只有第1、3图的步骤需要细分,但是我们说递归是在重复执行同一个步骤,那么我们就要把移动规律找出来。移动的规律其实就是当n=2时的移动步骤。后续n>2时都应该遵循这个步骤,否则就不是递归了。

图一:若要把图一A针上的n-1个盘子移动到B针,把B针和C针互换一下位置就变成A针上有n-1个盘子的情况,这样又可以重复递归的步骤

 图三:同理,若要把图三B针上的n-1个盘子移动到C针,把A针和B针互换一下位置就变成A针上有n-1个盘子的情况,然后再重复递归的步骤 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值