主题:有关汉诺塔
wabjx
[专家分:0] 发布于 2010-10-28 16:37:00
想问一下,下面这个用堆栈实现的汉诺塔程序跟用递归方法实现有什么不同吗,感觉差不多啊
class Hanoi{
friend void To w e r s O f H a n o i ( i n t ) ;
p u b l i c :
void TowersOfHanoi(int n, int x, int y, int z);
p r i v a t e :
Stack<int> *S[4];
} ;
void Hanoi::TowersOfHanoi(int n, int x, int y, int z)
{// 把n 个碟子从塔x 移动到塔y,可借助于塔z
int d; // 碟子编号
if (n > 0) {
TowersOfHanoi(n-l, x, z, y);
S[x]->Delete(d); //从x中移走一个碟子
S[y]->Add(d); //把这个碟子放到y 上
S h o w S t a t e ( ) ;
TowersOfHanoi(n-l, z, y, x);}
}
void TowersOfHanoi(int n)
{// Hanoi::towersOfHanoi的预处理程序
Hanoi X;
X.S[1] = new Stack<int> (n);
X.S[2] = new Stack<int> (n);
X.S[3] = new Stack<int> (n);
for (int d = n; d > 0; d--) // 初始化
X.S[1]->Add(d); // 把碟子d 放到塔1上
/ /把塔1上的n个碟子移动到塔3上,借助于塔2 的帮助
X . TowersOfHanoi(n, 1, 2, 3);
}
回复列表 (共6个回复)
沙发
eastcowboy [专家分:25370] 发布于 2010-10-28 19:27:00
这个代码虽然用了堆栈,但还是使用了递归。实际上如果用堆栈,就可以避免递归了。
板凳
wabjx [专家分:0] 发布于 2010-10-31 17:50:00
那请问楼上的,您写过用堆栈实现的汉诺塔程序吗,方便的话发一个看看,我刚刚学堆栈,许多东西还不太懂,谢谢啊
3 楼
eastcowboy [专家分:25370] 发布于 2010-10-31 23:50:00
还是从递归说起吧,相信楼主对递归方式的实现也比较熟悉了。下面是一个比较典型的递归实现:
[quote]/*
汉诺塔,递归版本
*/
#include <stack>
#include <cassert>
#define N 3
struct TOWER {
char name;
std::stack<int> content;
};
void solve(TOWER* fromTower, TOWER* toTower, TOWER* tmpTower, int n)
{
// state 0:
if (n == 0) {
return;
}
// state 1:
solve(fromTower, tmpTower, toTower, n - 1);
// state 2:
int d = fromTower->content.top();
assert(toTower->content.empty() || d < toTower->content.top());
fromTower->content.pop();
toTower->content.push(d);
printf("move %d from %c to %c\n", d, fromTower->name, toTower->name);
// state 3:
solve(tmpTower, toTower, fromTower, n - 1);
}
int main() {
TOWER a, b, c;
int i;
a.name = 'A';
b.name = 'B';
c.name = 'C';
for (i = N; i > 0; --i) {
a.content.push(i);
}
solve(&a, &b, &c, N);
return 0;
}[/quote]
主要就是solve函数采用了递归。其实所谓递归主要也是借用了计算机硬件的堆栈。如果我们在程序里面也定义一个堆栈,效果是一样的。
计算机硬件堆栈保存了什么信息?主要有三点。一是函数的参数,二是函数的局部变量,三是这个函数目前已经执行到什么位置了。了解了这个,只要定义一个struct,把所有的参数、局部变量、函数目前执行到的位置全部都装到这个struct里面,再定义这个struct的stack即可。对于本例来说,这个struct只需要放入函数的参数、目前执行到的位置,没有必要放入局部变量。
最后的程序像这个样子:
[quote]/*
汉诺塔,非递归版本
*/
#include <stack>
#include <cassert>
#define N 3
struct TOWER {
char name;
std::stack<int> content;
};
struct INFO {
TOWER* fromTower;
TOWER* toTower;
TOWER* tmpTower;
int n;
int state;
};
int main() {
TOWER a, b, c;
int i;
a.name = 'A';
b.name = 'B';
c.name = 'C';
for (i = N; i > 0; --i) {
a.content.push(i);
}
INFO info;
info.fromTower = &a;
info.toTower = &b;
info.tmpTower = &c;
info.n = N;
info.state = 0;
std::stack<INFO> stack;
stack.push(info);
while (!stack.empty()) {
INFO lastInfo = stack.top();
stack.pop();
INFO newInfo;
int d;
switch (lastInfo.state) {
case 0:
if (lastInfo.n == 0) {
break;
}
++lastInfo.state;
stack.push(lastInfo);
break;
case 1:
++lastInfo.state;
stack.push(lastInfo);
newInfo.fromTower = lastInfo.fromTower;
newInfo.toTower = lastInfo.tmpTower;
newInfo.tmpTower = lastInfo.toTower;
newInfo.n = lastInfo.n - 1;
newInfo.state = 0;
stack.push(newInfo);
break;
case 2:
++lastInfo.state;
stack.push(lastInfo);
d = lastInfo.fromTower->content.top();
assert(lastInfo.toTower->content.empty() || d < lastInfo.toTower->content.top());
lastInfo.fromTower->content.pop();
lastInfo.toTower->content.push(d);
printf("move %d from %c to %c\n", d, lastInfo.fromTower->name, lastInfo.toTower->name);
break;
case 3:
++lastInfo.state;
stack.push(lastInfo);
newInfo.fromTower = lastInfo.tmpTower;
newInfo.toTower = lastInfo.toTower;
newInfo.tmpTower = lastInfo.fromTower;
newInfo.n = lastInfo.n - 1;
newInfo.state = 0;
stack.push(newInfo);
break;
}
}
return 0;
}[/quote]
楼主对比一下,相信就能明白了。
4 楼
eastcowboy [专家分:25370] 发布于 2010-10-31 23:58:00
第一段代码的state 0, state 1, state 2, state 3,分别对应第二段代码的case 0, case 1, case 2, case 3。
递归调用,都用stack.push代替。而函数的参数传递则用赋值来代替。另外还增加了一个大的while循环。这样一系列的动作,就可以完全的替换掉递归了。
5 楼
wabjx [专家分:0] 发布于 2010-11-01 21:41:00
嗯,受益匪浅,谢谢楼上的。高手啊。
6 楼
wabjx [专家分:0] 发布于 2010-11-12 18:28:00
继续深究,假设我要移动三个盘子,理论上需要七步,如果七步之后我正好完成(所有盘子都移动到第三个盘子)则最好,但是中间如果有故意的移动错误,也就是违反了大在下小在上的规则,我怎么样把那步以后的步骤都省略,换句话说我怎么样来检测该步移动是否符合规则。举例3个盘子,A->C,A->B,C->B,A->C,B->A,B->C,A->C.需要这七步完成,如果我中间一步出错或者顺序错误,则不能把三个盘子从A移到C,比如A->C,A->B,A->C,则错了,把最大的压在最小的上面了,我怎么判别
我来回复