开始学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~n-1盘子从A移动到B, 此时以C为中转, 移动次数为Fn-1
- 把第n个盘子从A移动到C, 移动次数为1
- 把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');
}
可以看到我当时并不十分理解这个过程, 只是记忆了在这个代码中应该怎么写, 而这么写虽然是正确的, 但对汉诺塔移动的过程没有很清楚的描述.