1. 程式人生 > >cc150:使用棧來實現漢諾塔

cc150:使用棧來實現漢諾塔

       遞迴解法其實也是用到了棧的,在每次遞迴呼叫自己的時候, 將中間的狀態引數壓入棧中。不過這些操作都是系統隱式進行的, 所以你不用去關心它具體是怎麼壓棧出棧的。如果我們要用棧自己來實現這個過程, 就不得不考慮這其中的細節了。

      接下來,我們就顯式地用棧來實現遞迴過程中,這些狀態引數的壓棧出棧過程。首先, 我們需要定義一個數據結構來儲存操作過程中的引數。

struct op{
    int begin, end;
    char src, bri, dst;
    op(){

    }
    op(int pbegin, int pend, int psrc, int pbri, int pdst):begin(pbegin), end(pend), src(psrc), bri(pbri), dst(pdst){

    }
};

     其中的5個引數表示,在柱子src上有一疊圓盤,標號從begin到end, 要將它們從src移動到dst,中間可藉助柱子bri。end其實相當於遞迴解法中的n, src,bri,dst與遞迴解法中的對應。那為什麼還要定義begin這個變數呢? 為了判斷柱子上是否只剩下一個盤子。如果begin等於end, 說明柱子上只剩下“最後”一個圓盤,可以進行移動。當然了, 用另外一個布林變數來表示是否只剩下一個圓盤也是可以的,效果一樣。 講遞迴方法的時候,說到從初始狀態到最終狀態一共要經過以下幾個狀態:
(1~n, 0, 0)
(n, 1~n-1, 0)
(0, 1~n-1, n)
(0, 0, 1~n)

    這些過程我們現在需要自己壓棧出棧處理。壓棧的時候不做處理,出棧時進行處理。因此, 壓棧的時候需要與實際要操作的步驟相反。一開始,我們將最終想要完成的任務壓棧。 聽起來怪怪的,其實就是往棧中壓入一組引數:

stack<op> st;
st.push(op(1, n, src, bri, dst));

    這組引數表示,柱子src上有1~n個圓盤,要把它移動到dst上,可以藉助柱子bri。 當棧st不為空時,不斷地出棧,當begin和end不相等時,進行三個push操作 (對應上面四個狀態,相鄰狀態對應一個push操作,使狀態變化), push與實際操作順序相反(因為出棧時才進行處理,出棧時順序就正確了), 如果,begin與end相等,則剩下當前問題規模下的“最後”一個圓盤,直接列印移動方案, hanoi程式碼如下:
void hanoi(int n, char src, char bri, char dst){
    stack<op> st;
    op tmp;
    st.push(op(1, n, src, bri, dst));
    while(!st.empty()){
        tmp = st.top();
        st.pop();
        if(tmp.begin != tmp.end){
            st.push(op(tmp.begin, tmp.end-1, tmp.bri, tmp.src, tmp.dst));
            st.push(op(tmp.end, tmp.end, tmp.src, tmp.bri, tmp.dst));
            st.push(op(tmp.begin, tmp.end-1, tmp.src, tmp.dst, tmp.bri));
        }
        else{
            cout<<"Move disk "<<tmp.begin<<" from "<<tmp.src<<" to "<<tmp.dst<<endl;
        }

    }
}