汉诺塔问题 hanoi tower (递归)。

又来填坑了。
还是递归问题,这次是汉诺塔。
题意是这样的:有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子C上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问至少需要多少次移动,并输出移动的步骤。
由用户自定义输入金盘的个数n。
可以测试数据:当n=3时,自己模拟一下移动的步骤;
例如:
当A柱子上有3个从上往下按照从小到大依次排列盘子时,要把这3个金盘全部移动到C柱子上,且仍然是从上往下按照从小到大的放置顺序。步骤如下:
(记这3个盘子的编号为x,y,z,x在最上,y在中间,z在最下)
第一步:把 x 从A移动到C; 此时的情况(从上到下):A有yz,B有0,C有x
第二步:把 y 从A移动到B; 此时的情况(从上到下):A有z,B有y,C有x
第三步:把 x 从C移动到B; 此时的情况(从上到下):A有z,B有xy,C有0
第四步:把 z 从A移动到C; 此时的情况(从上到下):A有0,B有xy,C有z
第五步:把 x 从B移动到A; 此时的情况(从上到下):A有x,B有y,C有z
第六步:把 y 从B移动到C; 此时的情况(从上到下):A有x,B有0,C有yz
第七步:把 x 从A移动到C; 此时的情况(从上到下):A有0,B有0,C有xyz

移动完毕。
先讨论输出一共有多少步骤的这个问题。其实自己实验一下可以发现这是有规律的:
n 步数(记为t)
1 1
2 3
3 7
4 15
…………..
我做的时候发现除了当n=1时,t=1之外,当n=i,t=t’(n=i-1) * 2 +1
也就是n的步数等于n-1时的步数的两倍再+1。
于是我在输出步数的时候就又用了一个递归函数。

但是。。后来发现其实步数还等于2^n-1,也就是说这里可以不用递归函数,只要for循环一下,当计数小于n时,让2不断乘以2最后输出再-1就行了。

我在程序里一共定义了两个递归函数,一个是上面说的输出步数的函数,hanoi().
还有一个就是输出步骤的函数,hanoi_tower().
在汉诺塔函数里,传递的参数有盘子个数n,三根柱子分别为xyz

void hanoi_tower(int n,char x,char y, char z) //输出步骤
{
    if(n==1)
        move(1,x,z);
    else{
        hanoi_tower(n-1,x,z,y);
        move(n,x,z);
        hanoi_tower(n-1,y,x,z);
    }
}

补充另一个函数move:

void move(int n,char from,char to)
{
    printf("第 %2d 步:把第 %d 个盘子: %c >>>>>>> %c\n",ct++,n,from,to);
}

汉诺塔首先要考虑的是,移动到C ,那就要先把最大的盘子放置好在C中。但最大的盘子在A的最底层,所以要先借助B,把第n个盘子上面的n-1个盘子全部放到B中,同时C也要是空着的状态,这样才能把最大的盘子放在C的最底层,然后上面的盘子才有条件从大到小一个个放进C中。

假设现在的状态是A已经只剩下第n个盘子了,B从上往下按照从小到大的顺序依次放着n-1个盘子,C是空的。那么接下来的这一步就是把第n个盘子移到C中,然后情况就出现递归了,这时候的情况就相当于把n-1个盘子从B中移动到C,与初始状态相同。这样的情况放在函数中的体现就是只要把hanoi_tower中的参数,把原本的A跟B互换位置。

一直递归递归。。。函数的出口是当移动到C中已经有n-1个盘子,在函数中,n一直减到最后n==1时,也就是只差最后的,最小的那个盘子还没放到C上。最后一步就是直接move最后一个盘子,然后结束递归。

完整代码如下:

#include<stdio.h>
int ct=1;//记录步数,在步骤中输出
void move(int n,char from,char to)
{
    printf("第 %2d 步:把第 %d 个盘子: %c >>>>>>> %c\n",ct++,n,from,to);
}
int hanoi(int n)//输出步数:
{
    int cnt = 2,ans = 1;
    if(n == 1)
        return 1;
    else
        return 2* hanoi(n-1) +1;
}
void hanoi_tower(int n,char x,char y, char z) //输出步骤
{
    if(n==1)
        move(1,x,z);
    else{
        hanoi_tower(n-1,x,z,y);
        move(n,x,z);
        hanoi_tower(n-1,y,x,z);
    }
}
int main()
{
    int n;//盘子个数
    printf("输入盘子个数:\n");
    scanf("%d",&n);
    char x = 'A',y = 'B',z = 'C';
    int t = hanoi(n);
    printf("一共需要%2d步。\n",t);
    hanoi_tower(n,x,y,z);
    return 0;
}

编译结果:
《汉诺塔问题 hanoi tower (递归)。》

《汉诺塔问题 hanoi tower (递归)。》

我还试了一下输入n=64时,然后电脑一直算到了移动20万次还没停下来。。。于是搜索了一下这个问题,结果说当n=64时,需要移动2^64-2才能停下来。。。假如每秒钟移动一次,需要5845亿年以上。。而地球才活了45亿年。。。这么大的计算量,电脑的运算速度都得算好久。

    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/vit_rose/article/details/51464698
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞