漢羅塔問題(修改版)---遞歸和棧實現

漢羅塔問題是一個非常經典的算法,我們首先來研究一下修改的漢羅塔(簡化步驟),在後面我們將來講述經典的漢羅塔問題。

題目:
修改後的漢羅塔的規則:現在限制不能從最左側的塔直接移動到最右側,必需要經過中間;同時從最右側移動到最左測試,同樣必需經過中間;要求移動N層塔時,打印最優移動

1、用遞歸函數實現(從最左移動到最右)

分析:
- 當只有一層塔時,我們先需要將其從左移到中間,再從中間移動到右邊,共分爲兩步;如果它就在中間,那麼我們只需要將它移動到左或則右,一步就行;
- 當我們有N 層塔時,我們需要先將1~N-1層塔移動到右邊,然後移動第N層塔到中間,再將1~N-1層塔移動到最左邊,將N層塔由中間移動右邊;這樣,第N層塔就移好了
- 接下來重複上述步驟,將1~N-2層塔移到最右邊,將第N-1層塔移到最中間……(利用遞歸函數實現)

代碼實現:

void  _HanoiProblem1(int num,string from,string to,int& count)//num代表塔的層數,from代表開始移動的位置(left,mid,right),to 代表要移動到的位置
{
    if(num == 1)//我們只有一層塔
    {
        if(from.compare("mid")==0 || to.compare("mid")==0)//此時只需直接移動
        {
            count++;
            cout<<"move "<<num<<" from "<<from.c_str()<<" to "<<to.c_str()<<endl;
        }
        else
        {
            cout<<"move "<<num<<" from "<<from.c_str()<<" to "<<"mid"<<endl;
            count++;
            cout<<"move "<<num<<" from "<<"mid"<<" to "<<to.c_str()<<endl;
            count++;
        }
    }
    else//我們有多層塔
    {
        if(from.compare("mid")==0 || to.compare("mid")==0)
        {
            string another;
            if(from.compare("left")==0)
                another = "right";
            else
                another = "left";

            _HanoiProblem1(num-1,from,another,count);//例如從左移到中間,先將1~N-1層塔移動到右邊,
            cout<<"move "<<num<<" from "<<from.c_str()<<" to "<<to.c_str()<<endl;//再將第N層塔移到中間
            count++;
            _HanoiProblem1(num-1,another,to,count);//再將1~N-1層塔移到中間

        }
        else//從左移到右或從右移到左
        {
            _HanoiProblem1(num-1,from,to,count);//例如從左移到右,先將1~N-1層塔從左移動到右邊,
            cout<<"move "<<num<<" from "<<from.c_str()<<" to "<<"mid"<<endl;//再將第N層塔移到中間
            count++;
            _HanoiProblem1(num-1,to,from,count);//再將1~N-1層塔從右移動到左邊
            cout<<"move "<<num<<" from "<<"mid"<<" to "<<to.c_str()<<endl;//再將第N層塔從中間移到右邊
            count++;
            _HanoiProblem1(num-1,from,to,count);//再將1~N-1層塔從左移動到右邊
        }
    }
}
void HanoiProblem1(int num,string from,string to)
{
    int count = 0;
    _HanoiProblem1(num,from,to,count);
    cout<<"count is: "<<count<<endl;
}

測試代碼:

void funtest()
{
    HanoiProblem1(2,"left","right");
}

int main()
{
    funtest();
    getchar();
    return 0;
}

結果圖
這裏寫圖片描述

2.用棧模擬實現

分析:
我們上面用遞歸實現,我們已經知道了基本的走法,接下來我們用棧來模擬漢羅塔問題,將塔的移動轉換爲入棧和出棧的操作,但是,由題我們知道了參數入棧和出棧的兩個基本規則

  • 小壓大問題,即只有當要入棧的參數小於棧頂元素,這時我們才能入棧
  • 動作不想臨,題目要求我們實現最優移動,所以我們從左移動到中間,下一步將它從中間右移動到左邊,是沒有意義的

滿足了以上兩條規則,我們現在看移動的過程,一個塔a,只有四中可能的動作,從左到中,從中到右,從右到中,從中到左,但是要滿足以上兩種規則我們發現它只有一種動作可以走;例如:a在最左邊,第一次,它只能從左到中間,第二次,由規則知,從左到中間不行,從中間到左沒有意義,那麼只剩下從中間到右和從右到中間,我們只需判斷就能得到結果。

代碼實現:

enum action
{
    No,
    LToM,
    MToL,
    RToM,
    MToR,
};

int fStackToStack(action& record,action preAction,action nowAction,stack<int>& fstack,stack<int>& tstack,string from,string to)
{
    if(record != preAction && fstack.top() < tstack.top())//滿足兩條準則,動作不可逆和小壓大原則
    {
        int data = fstack.top();
        fstack.pop();
        tstack.push(data);
        cout<<"move "<<data<<" from "<<from.c_str()<<" to "<<to.c_str()<<endl;
        record = nowAction;
        return 1;
    }
    return 0;
}

int HanoiProblem2(int num,string left,string mid,string right)
{
    stack<int> ls;
    stack<int> ms;
    stack<int> rs;

    ls.push(INT_MAX);
    ms.push(INT_MAX);
    rs.push(INT_MAX);

    for(int idx=num; idx>0; --idx)
        ls.push(idx);

    action record = No;//記錄上一步的動作
    int count = 0;

    while(rs.size() != num+1)
    {
        count += fStackToStack(record,MToL,LToM,ls,ms,left,mid);
        count += fStackToStack(record,LToM,MToL,ms,ls,mid,left);
        count += fStackToStack(record,MToR,RToM,rs,ms,right,mid);
        count += fStackToStack(record,RToM,MToR,ms,rs,mid,right);
    }
    return count;
}

測試代碼:

void funtest()
{
    int ret = HanoiProblem2(2,"left","mid","right");
    cout<<ret<<endl;
}

int main()
{
    funtest();
    getchar();
    return 0;
}

結果圖:
這裏寫圖片描述

發佈了109 篇原創文章 · 獲贊 17 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章