汉诺塔问题C语言求解,以及一些有关递归问题的思考

首先提供全部代码,如果后面的内容不想看,可直接自取下面代码:

#include <stdio.h>

void hanoi(int n, char from, char aux, char to) {
    //如果只有一个盘子,直接从 from 移到 to
    //n 的数字由小到大,依次对应着盘子由上到下
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", from, to);
        return;
    }

    //多个盘子的情况,将上面的 n-1 个盘子移到辅助棍 aux,给最底下的盘子留下终点棍 to 的空间
    hanoi(n - 1, from, to, aux);

    //可以将最底下的盘子移到终点棍 to 了
    printf("Move disk %d from %c to %c\n", n, from, to);

    //剩下的 n-1 个盘子以辅助棍 aux 为起点,借助 from 移到 to
    hanoi(n - 1, aux, from, to);
}

int main() {
    int n;
    printf("Enter the number of disks: ");
    scanf("%d", &n);
    printf("\n");
    hanoi(n, 'A', 'B', 'C');
    return 0;
}

汉诺塔这个游戏大家肯定玩过,我这里贴一个图大家应该就能想到玩法:

 接下来介绍在C语言中,如何用函数递归解决这个问题。

        我们在写递归的时候,先不要试图用脑袋想出一个问题的所有步骤,这在很多时候是不现实的,例如汉诺塔问题,其步骤取决于盘子个数 n ,大小为2^n-1。很明显 n 如果为4 ,那么就会有15个步骤,那就很难想了。

        因此,先不要去想步骤,先要去做的是坚定的相信,自己写的递归函数可以解决自己想解决的问题,这样可以减少自己在写代码时的胡思乱想,避免在代码陷入死循环前,自己的脑袋先陷入死循环。

        下面开始解决汉诺塔问题:

        为了然问题能够适应更多的情况,用4个盘子进行举例。

        三个棍子分别起名为from、aux和to,对应于起点棍,辅助棍和终点棍。 

         这里面,上面三个盘子明显相对对于最底下的盘子是独立的,因而可以完全不考虑最底下的盘子进行移动,当然最底下的盘子也必须要等到上面三个动完才能动,它最大,其他任何一个盘子都不让他落在上面。而上面三个盘子,很明显要先移动到辅助棍 aux ,最后让最底下的盘子移动到终点棍 to 。忽略中间步骤,进而我们最终要得到的是这样一个画面:

         接着,将 aux 上的三个盘子移到 to 上面就大功告成了!!!(忽略上面三个盘子移动的过程)如图:

         从上面的步骤来看,将最上面的三个盘子移到 aux ,并从 aux 移到 to ,不就是再搞两次相对简单的汉诺塔问题么,也就是说,我们在自己写的汉诺塔函数中调用两次汉诺塔函数,这个问题不就解决了,只不过这个时候棍子的顺序变成了(from  to  aux)和(aux  from  to),接下来开始写代码:

void hanoi(int n, char from, char aux, char to) {

    //多个盘子的情况,将上面的 n-1 个盘子移到辅助棍 aux,给最底下的盘子留下终点棍 to 的空间
    hanoi(n - 1, from, to, aux);

    //可以将最底下的盘子移到终点棍 to 了
    printf("Move disk %d from %c to %c\n", n, from, to);

    //剩下的 n-1 个盘子以辅助棍 aux 为起点,借助 from 移到 to
    hanoi(n - 1, aux, from, to);
}

        这里面将函数命名为 hanoi ,并在内部两次调用 hanoi ,中间用 printf 函数模拟移动盘子的过程。正如我在前面提到的,要坚信自己的函数好用,因此我就按照步骤调用它就肯定没有问题,不要去想步骤细节。

        当然,上述函数是有问题的,如果 n 为1,也就是有一个盘子,那么 n-1 就是0,再用 hanoi 调用起来(我们假设它是现成好用的)就会有问题,因此我们要在前面加上 (1 == n)的情况:

void hanoi(int n, char from, char aux, char to) {
    //如果只有一个盘子,直接从 from 移到 to
    //n 的数字由小到大,依次对应着盘子由上到下
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", from, to);
        return;
    }

    //多个盘子的情况,将上面的 n-1 个盘子移到辅助棍 aux,给最底下的盘子留下终点棍 to 的空间
    hanoi(n - 1, from, to, aux);

    //可以将最底下的盘子移到终点棍 to 了
    printf("Move disk %d from %c to %c\n", n, from, to);

    //剩下的 n-1 个盘子以辅助棍 aux 为起点,借助 from 移到 to
    hanoi(n - 1, aux, from, to);
}

         这里面我们在(1 == n)时执行盘子1从 from 移动到 to 这个步骤,接下来我们很显然不想再继续调用 hanoi 了,因为已经没有可以移动的了,因此直接 return 出函数。这样 hanoi 函数就写好了,整体代码如下:

#include <stdio.h>

void hanoi(int n, char from, char aux, char to) {
    //如果只有一个盘子,直接从 from 移到 to
    //n 的数字由小到大,依次对应着盘子由上到下
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", from, to);
        return;
    }

    //多个盘子的情况,将上面的 n-1 个盘子移到辅助棍 aux,给最底下的盘子留下终点棍 to 的空间
    hanoi(n - 1, from, to, aux);

    //可以将最底下的盘子移到终点棍 to 了
    printf("Move disk %d from %c to %c\n", n, from, to);

    //剩下的 n-1 个盘子以辅助棍 aux 为起点,借助 from 移到 to
    hanoi(n - 1, aux, from, to);
}

int main() {
    int n;
    printf("Enter the number of disks: ");
    scanf("%d", &n);
    printf("\n");
    hanoi(n, 'A', 'B', 'C');
    return 0;
}

        运行一下:

如果有兴趣的话可以按照上面的步骤实践一下,结果肯定是对的。

        我们在遇到问题时,如果这个问题的解决过程中的一个环节是这个问题本身,只不过更简单了,但本质还是问题本身时,就可以用递归实现,实现方法就是直接调用函数本身。接着,我们要考虑的并不是一层一层的嵌套在内部是怎么实现的,而是调用以后的其它步骤,例如在汉诺塔问题中要考虑(1 == n)的情况,并且不要忘记 printf("Move disk %d from %c to %c\n", n, from, to); 这个步骤。

当然,下面这种写法:

void hanoi(int n, char from, char aux, char to) {

    if (n == 1)
    {
        hanoi(1, from, aux, to);
        return;
    }

    //多个盘子的情况,将上面的 n-1 个盘子移到辅助棍 aux,给最底下的盘子留下终点棍 to 的空间
    hanoi(n - 1, from, to, aux);

    //可以将最底下的盘子移到终点棍 to 了
    printf("Move disk %d from %c to %c\n", n, from, to);

    //剩下的 n-1 个盘子以辅助棍 aux 为起点,借助 from 移到 to
    hanoi(n - 1, aux, from, to);
}

        这种写法是不可以的,我们虽然坚信自己函数好用,但 hanoi(1, from, aux, to); 这种情况1是固定值是不变的,(n == 1)这个条件也是固定不变的还正好对应了那个1,从 if 下来以后明显会导致死循环,要特殊注意。在没有死循环的情况下,还是可以放心调用自身的,只要注意把东西写全就行,不要想太多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王bf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值