回 帖 发 新 帖 刷新版面

主题:有关汉诺塔

想问一下,下面这个用堆栈实现的汉诺塔程序跟用递归方法实现有什么不同吗,感觉差不多啊
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个回复)

沙发

这个代码虽然用了堆栈,但还是使用了递归。实际上如果用堆栈,就可以避免递归了。

板凳


那请问楼上的,您写过用堆栈实现的汉诺塔程序吗,方便的话发一个看看,我刚刚学堆栈,许多东西还不太懂,谢谢啊

3 楼

还是从递归说起吧,相信楼主对递归方式的实现也比较熟悉了。下面是一个比较典型的递归实现:
[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 楼

第一段代码的state 0, state 1, state 2, state 3,分别对应第二段代码的case 0, case 1, case 2, case 3。
递归调用,都用stack.push代替。而函数的参数传递则用赋值来代替。另外还增加了一个大的while循环。这样一系列的动作,就可以完全的替换掉递归了。

5 楼


嗯,受益匪浅,谢谢楼上的。高手啊。

6 楼


继续深究,假设我要移动三个盘子,理论上需要七步,如果七步之后我正好完成(所有盘子都移动到第三个盘子)则最好,但是中间如果有故意的移动错误,也就是违反了大在下小在上的规则,我怎么样把那步以后的步骤都省略,换句话说我怎么样来检测该步移动是否符合规则。举例3个盘子,A->C,A->B,C->B,A->C,B->A,B->C,A->C.需要这七步完成,如果我中间一步出错或者顺序错误,则不能把三个盘子从A移到C,比如A->C,A->B,A->C,则错了,把最大的压在最小的上面了,我怎么判别

我来回复

您尚未登录,请登录后再回复。点此登录或注册