对汉诺塔_递归问题的新理解

开始学C++函数是看到汉诺塔问题觉得好难理解, 当时记了记代码, 了解了些原理就跳过了.昨天时隔许久重新写汉诺塔, 错误百出. 查阅资料, 在现有的知识基础上有了更多更深的理解.

这次对汉诺塔问题的过程以及对应代码有了明确认识
递归或者说递推有了进一步认识

写这篇博客除了加深相关认识和记录之外, 还有对以前的纠错和反思

知乎上有很多关于汉诺塔问题的理解, 有的非常形象和详细

汉诺塔递归的精髓:
舍弃对全部过程的推导, 而局限于两步之间的关系!
其实这也是理解所有递归问题的关键.

那么现在的我是如何理解汉诺塔的呢?

我们知道汉诺塔移动次数是一个递推公式

Fn = 2*Fn-1 + 1
Fn = 2^n - 1

根据上面的公式
关于Fn过程我们可以理解为进行两个Fn-1过程, 再加一

为什么这么做?

假设我们要从A到C移动n个盘子, 那么中间某个过程必然存在1~n-1个盘子在B处, 第n个盘子在A处, 移动一次把它从A移动到C, 然后把n-1个盘子从B全部移动到C

我们可以解析出三个过程:

  1. 把1~n-1盘子从A移动到B, 此时以C为中转, 移动次数为Fn-1
  2. 把第n个盘子从A移动到C, 移动次数为1
  3. 把1~n-1盘子从B移动到C, 此时以A为中转, 移动次数为Fn-1

值得注意的是, n个盘子从第一处处移动到第二处, 中转为第三处, 这个过程和第一处第二处第三处是A还是B或C是无关的. 和第n+1个盘子也无关, 所以, 可以形象地理解为, 移动了n个盘子, 突然原先的地方又冒出一个盘子, 然后移动这个盘子, 再把之前的n个盘子进行移动.

C++代码为:

#include <iostream>

using namespace std;

void hanoi(int n, char from, char buffer, char to)
{
    if( n == 1 ) {
        cout << "From " << from << " to " << to << endl;
    } else {
        hanoi(n - 1, from, to, buffer);
        hanoi(1, from, buffer, to);
        hanoi(n - 1, buffer, from, to);
    }
}

int main()
{
    int n;
    char from = 'A', buffer = 'B', to = 'C';
    cin >> n;
    hanoi(n, from, buffer, to);
}

对此代码中hanoi函数的理解尤其重要, 在里面n != 1时调用了三次hanoi, 即三次递归, 分别代表上面说明的三个过程.

函数的参数变化是移动过程中中转位置的变化.
如: hanoi(n - 1, from, to, buffer);中, 中转位置是to

这段代码是标准的汉诺塔问题求解方法

下面是我之前的汉诺塔代码:

#include <iostream>

using namespace std;

void hanoi(int n, char a, char b, char c)
{
    if( n == 1 ) {
        cout << a << " -------> " << c << endl;
        return;
    }
    hanoi(n-1, a, c, b);
    cout << a << " -------> " << c << endl;
    hanoi(n-1, b, a, c);
}

int main()
{
    int n;
    cin >> n;
    hanoi(n, 'A', 'B', 'C');
}

可以看到我当时并不十分理解这个过程, 只是记忆了在这个代码中应该怎么写, 而这么写虽然是正确的, 但对汉诺塔移动的过程没有很清楚的描述.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值