河内的塔问题

时间:2010-12-19 01:57:08

标签: c++ recursion towers-of-hanoi

我读了一些关于河内塔问题的讨论。我使用以下代码理解递归解决方案:

void Hanoi3(int nDisks, char source, char intermed, char dest)
{
    if(nDisks == 1){
        cout << "Move the plate from " << source << " to " << dest << endl; 
    }
    else{
        Hanoi3(nDisks - 1, source, dest, intermed); 
        cout << "Move the plate from " << source << " to " << dest << endl; 
        Hanoi3(nDisks - 1, dest, intermed, source); 
    }
}

我实际需要做的是在每一步输出塔的某种类型的“插图”。我在完成这项工作时遇到了很多麻烦。我们的教师建议使用堆栈来跟踪哪个磁盘在哪个磁盘上,但是我输出这个时很难找到并输出堆栈中的值需要弹出顶部条目并删除它们。如果我理解正确,他们会迷路。

无论哪种方式,它都让我得到了一些像这样开始的解决方案:

void Hanoi(int nDisks, stack<int> source, stack<int> intermed, stack<int> dest){
    if(nDisks == 1){
        dest.push(source.top()); 
        source.pop(); 
    }
    else{
        Hanoi(nDisks - 1, source, dest, intermed); 
        dest.push(source.top()); 
        source.pop(); 
        Hanoi(nDisks - 1, dest, intermed, source); 
    }
}

int main()
{

    int nDisks; 
    cout << "Enter the number of disks:    "; 
    cin >> nDisks; 

    stack<int> source, intermed, dest; 

    for(int i = nDisks; i >= 1; i--){
        source.push(i); 
    }

    Hanoi(nDisks, source, intermed, dest); 

    return 0;
}

我很清楚这是错的。我不确定用磁盘编号填充源代码的好地方。而且我每次都传递相同大小的源堆栈。如果有人可以给我一些方向或任何东西,那真的很酷。谢谢。

3 个答案:

答案 0 :(得分:3)

传递对堆栈的引用:

stack<int>&

还要考虑使用向量而不是堆栈,以便您可以迭代它以查看塔的当前内容。

PigBen的回答还正确地识别了代码中的一个错误,即您没有将磁盘从中间塔移动到目标塔。

答案 1 :(得分:1)

通过引用传递堆栈,并在最后一步中更改传递它们的顺序,以便您使用source作为中间文件,从中间移动到dest。

void Hanoi(int nDisks, stack<int> &source, stack<int> &intermed, stack<int> &dest){
    if(nDisks == 1){
        dest.push(source.top()); 
        source.pop(); 
    }
    else{
        Hanoi(nDisks - 1, source, dest, intermed); 
        dest.push(source.top()); 
        source.pop(); 
        Hanoi(nDisks - 1, intermed, source, dest); 
    }
}

要显示堆栈,只需将其复制并从副本中弹出。

答案 2 :(得分:1)

考虑您的原始代码:

void Hanoi3(int nDisks, char source, char intermed, char dest)
{
    if(nDisks == 1){
        cout << "Move the plate from " << source << " to " << dest << endl; 
    }
    else{
        Hanoi3(nDisks - 1, source, dest, intermed); 
        cout << "Move the plate from " << source << " to " << dest << endl; 
        Hanoi3(nDisks - 1, dest, intermed, source); 
    }
}

这不正确,所以这可能是你的老师建议展示塔楼的原因。

正如您所注意到的,std::stack是一个很好的容器,用于表示塔的当前磁盘,但正如您还要注意的那样,在没有弹出的情况下获取std::stack的元素并不是完全直截了当的他们。你可以弹出它们并将它们向下推到另一个堆栈上,然后将它们移回去,但这是错综复杂和愚蠢的,更不用说一般情况下效率低下了。这就是std::stack有一个protected成员c(基础容器)的原因,您可以通过从类中派生来访问它。

std::stack中没有虚拟成员,因此拥有protected成员的唯一目的是让它稍微难以理解。我认为这是一个糟糕的设计决定。但这就是你所拥有的,所以,

#include <iostream>
#include <string>
#include <stack>
#include <stddef.h>
using namespace std;

typedef ptrdiff_t   Size;
typedef Size        Index;

class IntStack
    : public stack<int>
{
public:
    Size count() const { return size(); }
    int at( Index i ) const { return c[i]; }
};

class Hanoi
{
private:
    IntStack    towers_[3];
    int         nDisks_;

public:
    Hanoi( int nDisks )
        : nDisks_( nDisks )
    {
        for( int disk = nDisks;  disk >= 1;  --disk )
        {
            towers_[0].push( disk );
        }
    }

    IntStack const& towers( Index i ) const { return towers_[i]; }
};

int main()
{
    int const   nDisksTotal = 2;

    Hanoi   puzzle( nDisksTotal );

    for( Index i = 0;  i < 3;  ++i )
    {
        IntStack const& tower   = puzzle.towers( i );
        Size const      nDisks  = tower.count();

        cout << "Tower " << i << ": ";        
        for( Index diskPos = 0;  diskPos < nDisks;  ++diskPos )
        {
            if( diskPos > 0 ) { cout << ", "; }
            cout << tower.at( diskPos );
        }
        cout << endl;
    }
}

请注意,此代码仅说明了如何访问这些堆栈的元素。

可以使显示更加花哨,例如图形。

我建议你让你的求解器函数成为类Hanoi的成员。并添加成员函数以显示拼图状态。稍后您可能希望将其转换为回调,以支持图形用户界面。

编辑:嗯,我看到另一个答案显示了该问题的“解决方案”。这消除了OP的学习经历以及展示塔楼的全部原因。这个答案故意只是肯定了这个错误是真实的,而是显示了OP要求的内容,即如何有效地显示堆栈。

干杯&amp;第h。,