理解汉诺塔中的递归

理解汉诺塔中的递归

##先从一个简单的例子,看看递归的效果。

#include <stdio.h>
void re(int n)
{
    printf("%d",n);
    if(n==1)
    {
        printf("%d",1);
    }
    else
    {
        re(n-1);
    }
    printf("%d",n);
}
int main()
{
    int n;
    scanf("%d",&n);
    re(n);
    return 0;
}

当n输入为5的时候

通过以上代码容易发现,此处递归是将n倒推到1,然后再从1~n的结果按照所给的递归公式算出。

在本例中,输出中的第一部分:5 4 3 2 1(是递归过程中输出的),第二部分:1(递归结束的点输出),第三部分:1 2 3 4 5(是根据第一部分得出的递归公式输出的。

注意:n==1的情况

    if(n==1)
    {
        printf("%d",1);
    }
	即递归结束的判断条件,如果此处不写,递归就会一直进行下去。
你会看到如下情况:

死循环

下面附上一位知乎(李冰)上的一种比较有趣的讲解:https://www.zhihu.com/question/20507130

说了这么多,回归正题。
约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到中间的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。
输入
输入为一个整数(小于20)后面跟三个单字符字符串。

整数为盘子的数目,后三个字符表示三个杆子的编号。

输出
输出每一步移动盘子的记录。一次移动一行。

每次移动的记录为例如 a->3->b 的形式,即把编号为3的盘子从a杆移至b杆。
样例输出
a->1->c
a->2->b
c->1->b

先附上我的代码:

#include<bits/stdc++.h>
using namespace std;
int n;
void han(int n,char a,char b,char c)
{
    if(n==1)
    {
        printf("%c->%d->%c\n",a,n,b);
    }
    else
    {
        han(n-1,a,c,b);//先将a中的n-1个盘子放到c盘中,此时a还剩一个最大的盘子,c中有n-1个已经按顺序放好的盘子
        printf("%c->%d->%c\n",a,n,b);//将a中剩下的最大的盘子放到目标b盘中。
        han(n-1,c,b,a);//将c盘中的n-1个盘子放到目标b盘中。
    }
}
int main()
{
    char a,b,c;
    scanf("%d %c %c %c",&n,&a,&b,&c);
    han(n,a,b,c);
    return 0;
}

此处我以输入n=3为例,
在这里插入图片描述
在这里插入图片描述

总结

1.将问题层层剥皮,理清思路。有点像俄罗斯套娃。
2.实现代码时注意结束判断

第一次写博客,如有错误还请诸位大佬指出。谢谢

<think>嗯,用户说还是不理解汉诺塔递归思想。我之前应该已经解释过递归的基本步骤,但用户可能还没完全明白。需要换个角度或者更详细地分解步骤。 首先,得确认用户是否理解递归的基本概念。汉诺塔问题是一个经典的递归案例,可能用户对递归本身的理解还不够深入,导致无法将递归应用到具体问题上。得从基础入手,再结合具体例子。 汉诺塔问题是将n个盘子从A柱移动到C柱,中间借助B柱。每次只能移动一个盘子,且大盘子不能在小盘子上面。递归的关键在于分解问题。假设已经知道如何移动n-1个盘子,那就可以分三步:先移动n-1个到B,再移动最大的到C,最后把n-1个从B移到C。 用户可能卡在如何将大问题分解成小问题这里。比如,当n=3的时候,具体的步骤是怎样的?或者他们可能无法想象递归调用的堆栈过程,导致觉得抽象。 或许需要用更直观的例子,比如n=1、n=2的情况,再扩展到n=3。展示每一步的分解,让用户看到递归是如何逐步解决问题的。同时,强调递归的终止条件,也就是当只剩一个盘子的时候直接移动,这是递归的基础。 另外,可能用户对函数自我调用的过程感到困惑。比如,当函数调用自身时,参数的变化和柱子角色的转换(比如借助柱的变化)。这时候需要详细解释每次递归调用时,源柱、辅助柱、目标柱是如何变化的,以及为什么需要这样转换。 还可以用比喻,比如把移动n个盘子想象成移动上面的n-1层和最后一层,然后递归处理每一层。或者用树状图来表示每次递归调用的分解过程,帮助用户形象化理解。 可能需要检查用户是否真的理解递归的两个关键部分:递归步骤和终止条件。在汉诺塔问题中,终止条件是当n=1时直接移动;递归步骤是把问题分解为n-1和1的组合。用户可能没有意识到这两个部分是如何协同工作的。 另外,可以指出常见的误区,比如试图跟踪每一个具体的移动步骤,而不是信任递归已经处理了子问题。递归依赖于子问题的解决,不需要关心子问题内部的具体步骤,只需要相信它们已经被正确解决,这可能是用户需要转变思维的地方。 总结来说,应该分步骤解释,从简单例子开始,强调分解问题和递归终止条件,解释参数变化,并用比喻或图示辅助理解。可能还需要对比递归和迭代的不同,帮助用户建立递归的思维方式。</think>好的,我们通过一个层层分解的视角,用「搭积木」的比喻来理解汉诺塔递归思想: --- ### **一、先记住核心逻辑** 递归的核心就一句话:**把前n-1层看作一个整体,最后一层单独处理**。就像搭积木时: 1. 把上面所有积木搬到「临时存放区」 2.把最底下的积木搬到「目标位置」 3.再把所有积木从「临时存放区」搬到「目标位置」 --- ### **二、用n=3的实例推演** 假设有3层汉诺塔(A→C),分解步骤: 1. **前2层:A→B(借助C)** - 此时问题简化为:如何移动2层?继续递归分解: - 第1步:把第1层从A→C - 第2步:把第2层从A→B - 第3步:把第1层从C→B 2. **第3层:A→C** 3. **前2层:B→C(借助A)** - 重复类似步骤:把第1层B→A,第2层B→C,第1层A→C 整个过程实际发生了7次移动,但**递归不需要你记住具体步骤,只需要信任「前n-1层总能正确移动」**。 --- ### **三、递归函数的关键参数** 函数定义为 `move(n, 起点, 终点, 缓冲区)`: ```python def move(n, A, C, B): if n == 1: print(f"{A}→{C}") # 终止条件:直接移动 else: move(n-1, A, B, C) # 关键!把前n-1层搬到缓冲区 move(1, A, C, B) # 移动最后一层 move(n-1, B, C, A) # 关键!把缓冲区积木搬到终点 ``` **注意参数顺序的对称性**:第二步的缓冲区(B)在第三步变成了起点,这就是递归的「角色互换」逻辑。 --- ### **四、递归的思维突破点** 1. **忽略细节**:不要纠结「前n-1层如何移动」,只需知道它们能完成 2. **角色转换**:每次递归调用时,「起点/终点/缓冲区」的角色会动态变化 3. **数学归纳法**:如果n=1成立,且假设n-1成立时n也成立,则对所有n成立 --- ### **五、用树状图理解调用过程(n=3)** ``` move(3,A,C,B) / | \ move(2,A,B,C) A→C move(2,B,C,A) / | \ / | \ ... A→B ... ... B→C ... ``` 每一层递归都在解决更小的子问题,最终像拼图一样组合成完整解。 --- ### **总结** 递归思维就像**「信任链」**:你只需要保证基础情况正确(n=1),并相信解决小问题后,自然能组合出大问题的解。汉诺塔递归之美,就在于用极简的代码表达了无限的分形结构。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值