汉诺塔问题
面试官:知道汉诺塔问题吗
菜鸡(我):知道,很简单,本质思想是递归
面试官:嗯…那写一下算法吧
菜鸡(我):…(咋写来着)
面试官:…(不会吧这都不知道)
背景
来源于百度百科
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如图1)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
约定
-
汉诺塔的三个柱子我分别定义为beginTower(A杆)、auxiliaryTower(B杆)、targetTower(C杆)
-
通过游戏规则不难发现,汉诺塔的杆子上的金盘只有上一层的被取出,才能取下一层,这符合先入后出的规则,因此使用 栈 表示汉诺塔的一根柱子
-
使用 栈 中的一个元素表示金盘,金盘的数值对应于元素的大小,金盘越大,元素的数值越大。
分析
只有一个元素(1)的情况
直接将元素1从beginTower中弹出,并压入到targetTower中
两个元素(1,2)的情况
- 将元素1从beginTower中弹出,并压入到auxiliaryTower中
- 将元素2从beginTower中弹出,并压入到targetTower中
- 将元素1从auxiliaryTower中弹出,并压入到targetTower中
三个元素(1,2,3)的情况
(长图警告)
- 将元素1从beginTower中弹出,并压入到targetTower中
- 将元素2从beginTower中弹出,并压入到auxiliaryTower中
- 将元素1从targetTower中弹出,并压入到auxiliaryTower中
- 将元素3从beginTower中弹出,并压入到targetTower中
- 将元素1从auxiliaryTower中弹出,并压入到beginTower中
- 将元素2从auxiliaryTower中弹出,并压入到targetTower中
- 将元素1从beginTower中弹出,并压入到targetTower中
规律:
- beginTower中只有一个元素时,直接将该元素放入targetTower中
- beginTower有n个元素时:
- 将beginTower中的n-1个元素放入auxiliaryTower,然后将beginTower最底层元素放入targetTower
- 将auxiliaryTower中的n-2个元素放入beginTower,然后将auxiliaryTower最底层的元素放入targetTower
- 再将beginTower中的n-3个元素放入auxiliaryTower,然后将beginTower最底层元素放入targetTower
- …以此类推
算法
按照上面的规律,可以写出以下伪代码:
Hanoi(n, beginTower, auxiliaryTower, targetTower){
① if(n>1)Hanoi(n, beginTower, auxiliaryTower, targetTower)//如果金盘数量大于一,将前n-1个金盘移动到auxiliaryTower上
② move_to(beginTower,targetTower)//将beginTower中的最底层的金盘移动到targetTower中
③ if(n>1)Hanoi(n, beginTower, auxiliaryTower, targetTower)//如果金盘数量大于一,将前n-1个金盘从auxiliaryTower移动到targetTower上
}
实现
#include <stack>
#include <iostream>
using namespace std;
void HanoiTower(int n,stack<int>& beginTower, stack<int>& auxiliaryTower, stack<int>& targetTower) {
//将前n-1层移动到auxiliaryTower上
if (n> 1)HanoiTower(n - 1, beginTower, targetTower, auxiliaryTower);
//将第n层移到targetTower上
targetTower.push(beginTower.top());
beginTower.pop();
//将前n-1层从auxiliaryTower移动到targetTower上
if (n> 1)HanoiTower(n - 1, auxiliaryTower, beginTower, targetTower);
}
int main() {
stack<int> beginTower;
stack<int> auxiliaryTower;
stack<int> targetTower;
for (int i = 10; i > 0; --i) {
beginTower.push(i);
}
HanoiTower(10,beginTower, auxiliaryTower, targetTower);
while (!targetTower.empty()) {
cout << targetTower.top() << endl;
targetTower.pop();
}
}