2010-12-19 62 views
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); 
    } 
} 

我真正需要做的是輸出某種類型的每一步塔的「插圖」的。完成這件事我有很多麻煩。我們的教師建議使用堆棧來跟蹤磁盤在哪個塔上,但是我無法輸出,因爲查找並輸出堆棧中的值需要彈出頂部條目並將其刪除。如果我理解正確,他們會迷失方向。

無論哪種方式,它使我的一些解決方案,開始了這樣的:

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; 
} 

我很清楚地知道,這是錯誤的。我不確定一個好的地方是用磁盤編號填充源代碼的地方。而且我每次都會傳遞相同大小的源代碼堆棧。如果有人可以給我一些方向或任何東西,這將是非常酷的。謝謝。

+1

如果u谷歌漢諾塔,或爲此事只是想想而已,你會發現簡單的非遞歸方法(S);-) – 2010-12-19 02:02:19

+0

我想,但我m應該使用遞歸方法。那可能嗎? – d2jxp 2010-12-19 02:05:13

回答

3

傳遞一個參考堆棧:

stack<int>& 

也可以考慮使用載體,而不是堆棧,所以你可以遍歷它看到塔的當前內容。

PigBen的回答也正確地識別了您的代碼中的一個錯誤,您沒有將磁盤從中間塔移動到目標塔。

+0

你能再詳細一點嗎? – d2jxp 2010-12-19 02:32:10

+1

這是作業,對吧?閱讀一些關於通過價值或引用傳遞參數的內容來理解爲什麼你「失去」你對堆棧的改變。 – 2010-12-19 02:44:47

+0

我剛剛解決了一個處理能夠檢查堆棧內容的方法 - 不要使用堆棧的其他問題... – 2010-12-19 02:50:31

1

通過引用傳遞堆棧,並在最後一個步驟中更改您傳遞它們的順序,以便將源代碼作爲中間源從intermed移動到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); 
    } 
} 

要顯示堆棧,只需複製它並從副本中彈出即可。

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所要求的內容,即如何有效地顯示堆棧。

乾杯&心連心,

+0

+1以正確的方式查找和討論錯誤;但我真的認爲處理堆棧接口不提供對其他元素的訪問的正確方法是**不使用堆棧**。這個包裝實際上沒有多少用處。它所做的只是記錄你只使用容器界面的一小部分的願望 - 而我們在這裏沒有這種願望。我不相信面向整體堆棧的面向對象方法確實有助於代碼的正確寫入。 – 2010-12-19 04:43:58

+0

@Karl:謝謝。堆棧是合適的(正如OP的指導者所建議的)是因爲遞歸算法僅在磁盤塔上使用堆棧操作,即彈出和推送。如果不使用'std :: stack',則必須自己實現堆棧,才能顯示狀態。使用標準庫提供的即用型堆棧更簡單。 :-) – 2010-12-19 05:02:55

+0

事情是,「實現」'std :: stack'由簡單地在你喜歡的連續容器上使用.push_back(),.pop_back()和.back()組成。容器適配器專門設計用於限制已經存在的功能。獲得這種效果的簡單方法是不使用你不想要的功能。 (這個原則當然可以用於極端情況:有些語言不會打擾'公共'或'私人',因爲我們都是成年人,並且理解訪問無證件數據成員是不調皮的。) – 2010-12-19 05:22:56