汉罗塔问题(c语言)

(时间:2021-5-29 10:00~11:30  汉罗塔问题)

今天上午,我花一番功夫平静下自己的内心,下定决心把汉罗塔问题解决掉。终于又花了一个半小时总算弄懂了这个问题,也加深了自己对函数的理解。

(题外话:本人算一个c语言小白吧,写这个是想理清一下自己的思路,加深一下自己的印象。第一次写这个,可能有些不对的地方,希望能得到你们的指正,也希望能帮助一些人更好的理解汉罗塔问题和函数参数。

额,学的不是很深,就用了高中学的知识理解递归函数。)

 

汉罗塔问题是一个典型的递归问题,递归拆开来看是传递与归还。传递是把n用n-1来表示,n-1 用n-2来表示……一层层的传递下去,归还是把传到最底层的f(1)的值一层层的归还回来,得到最终想要的结果。我把传递这个过程看做是数学问题中就是一个数列的问题,用递推公式表示,并写出a1=?,即递归函数的结束条件。而我们要敲得代码也就是如何把f(n)分解得到f(1),计算机会帮我们归还数值,最后的到我们想要的结果。

————————————————-—上代码:————————————-————————

 

#include <stdio.h>

int times(int n)          //定义函数times,计算要移动的次数
{
    int t;
    if(n==1)
    return 1;
    else
    t=times(n-1)*2+1;    
    return t;            
}

void Hanoi(int n,char begin,char end,char through) 
                         //定义函数Hanoi函数,把每次移动的具体步骤打印出来
{
    if(n==1)
        printf("%c->%c\n",begin,end);

    else
    {
        Hanoi(n-1,begin,through,end);
        printf("%c->%c\n",begin,end);
        Hanoi(n-1,through,end,begin);
    }
}

int main()
{
    int n;
    printf("输入盘子数:");
    scanf("%d",&n);
    printf("移动%d个盘子的步骤:\n",n);
    Hanoi(n,'A','C','B');
    printf("共%d步!",times(n));
    return 0;
}

 

————————————————————代码解释————————————————

     第一个函数是计算要移动的次数,直接用数列的递推公式表达。

     第二个函数是打印出每次移动情况。把问题分解,要想移动n层塔,需要做的是,想把上面n-1层塔先全部移到b柱,再把第n层塔移到c柱,接着把上面的n-1层塔移到c柱。而要把n-1层塔移到b柱,需要的是把上面的n-2层塔先移到a柱,再把第n-1层塔移到b柱,接着把上面的n-2层塔移到b柱。以此类推,可以把问题逆向拆解,即可用递归函数解决问题。(递)

void Hanoi(int n,char begin,char end,char through) 
{
    if(n==1)
        printf("%c->%c\n",begin,end);  //当n为1时,把第一块从a柱移到c柱

    else
    {
        Hanoi(n-1,begin,through,end);  //上面的n-1层利用c柱从a柱移到b柱
        printf("%c->%c\n",begin,end);  //第n层从a柱移到c柱
        Hanoi(n-1,through,end,begin);  //上面的n-1层利用a柱从b柱移到c柱
    }
}

当时,我疑惑的是为什么Hanoi函数后有begin、through、end这三个参数,而且位置还有些变化

        Hanoi(n-1,begin,through,end);
        printf("%c->%c\n",begin,end);
        Hanoi(n-1,through,end,begin);

第一个问题:为什么要设这三个参数?

因为在移动的过程中,虽然有两次都是把n-1个塔块看做一个整体移动,但他们的起始位置不同,一个是从a到b,一个是从b到c,移动的位置不同打印出来的也应该不同。

那,可以把它直接用printf打印出来不?我觉得不能。假设能的话,要在哪里添加printf语句呢,在else语句块中的第一个Hanoi后面直接加吗?那结果就只是a->b,a->c,b->c之间的循环了,完全不对。所以,应该把这三个设成参数,在塔数变化时,对应的步骤也会相应的变化。n为奇数与n为偶数时,其余的n-1会在不同的位置,在b柱上或在c柱上,这时就有可能把a柱当做暂放柱了,那么就还会有b->a,c->a这样的步骤出现。

那第二个问题来了:为什么要三个参数位置不同呢?

就如第一个问题第一句话所说的,起始位置不同。

那,为什么把三个参数的位置交换一下,就会出现b->a,c->a这样的步骤呢?我用的数学中的函数来理解(我自己觉得这样理解容易些)

假设一个二元函数f(x,y)= x^2 + 3y + 1

那么f(y,x)=y^2 + 3x +1,

x,y只是自变量,函数表达式具体怎样要根据对应位置上的参数来确定

类比数学中的函数,这个递归函数中的表达式 是   printf("%c->%c\n",begin,end);  输出的结果与第一和第二个参数有关,所以两次自调函数的参数位置不同。而且,递归函数本就是一个特殊的嵌套函数,每一层都与外面的函数有关,当外函数参数变化时,相应的内函数也会有所变化,就能实现b->a,c->a了。

 

结束语:非专业解答,只是自己的一种类比理解c语言代码。

上一篇:C语言递归函数


下一篇:正则表达式 与 re模块