關於棧與遞迴求解迷宮與迷宮最短路徑問題
一、棧實現迷宮問題:
問題描述:用一個二維陣列模擬迷宮,其中1為牆,0為通路,用棧方法判斷迷宮是否有出口,下圖為簡單模擬的迷宮:
思想:
1.首先給出入口點,如上圖入口點座標為{2,0};
2.從入口點出發,在其上下左右四個方向試探,若為通路(值為0)時,則向前走,並將每次通路結點入棧(push)儲存和標記走過的路為2;
3.當四個方向都沒有通路時,則開始回溯,將棧中儲存的結點開始pop,並將每個pop位置標記為3,直到在一個位置重新找到新的路,若沒有新路,棧最終將為空,即迷宮沒有出口;
4.當棧不為空,並且如在上圖例子中找到位置{9,2},即其橫座標=行數-1時,則找到迷宮出口,此時棧中儲存著這條通路路徑。
程式碼實現:
結果顯示:#include <iostream> #include <cstdlib> #include <assert.h> #include <stack> using namespace std; const size_t N=10; struct Pos { int _row; int _col; }; bool CheakIsAccess(int *maze,size_t n,Pos pos) //判斷每走一次是否為通路(棧) { if((maze[pos._row*n+pos._col]==0)&&(pos._row>=0&&pos._row<n)&&(pos._col>=0&&pos._col<n)) { return true; } return false; } bool GetMazePath(int *maze,size_t n,Pos entry,stack<Pos> &path) //棧方法判斷迷宮是否有出口 { assert(maze); Pos cur=entry; //cur儲存當前位置 path.push(cur); Pos next=cur; //next儲存下一個位置 while(!path.empty()) { cur=path.top(); maze[cur._row*n+cur._col]=2; //標記走過的路 if(cur._row==n-1) //找到一條通路 { return true; } //試探法:上下左右判斷是否有通路 //上 next=cur; next._row-=1; if(CheakIsAccess(maze,n,next)) { path.push(next); continue; } //右 next=cur; next._col+=1; if(CheakIsAccess(maze,n,next)) { path.push(next); continue; } //下 next=cur; next._row+=1; if(CheakIsAccess(maze,n,next)) { path.push(next); continue; } //左 next=cur; next._col-=1; if(CheakIsAccess(maze,n,next)) { path.push(next); continue; } maze[cur._row*n+cur._col]=3; //若四個方向都沒有通路為死路時標記為3 path.pop(); //並且回溯 } return false; } void GetMaze(int *maze,size_t N) //獲取迷宮 { FILE *fout=fopen("MazeMap.txt","r"); //在檔案中讀取 assert(fout); for(size_t i=0;i<N;++i) { for(size_t j=0;j<N;) { int value=fgetc(fout); if(value=='1'||value=='0') { maze[i*N+j]=value-'0'; ++j; } else if(value==EOF) { cout<<"Error(maze)"<<endl; fclose(fout); return; } } } fclose(fout); } void PrintMaze(int *maze,size_t N) //列印迷宮 { for(size_t i=0;i<N;++i) { for(size_t j=0;j<N;++j) { cout<<maze[i*N+j]<<" "; } cout<<endl; } } void TestMaze() { int maze[N][N]; GetMaze((int*)maze,N); PrintMaze((int*)maze,N); //列印迷宮 cout<<endl; stack<Pos> path; Pos entry={2,0}; //入口 maze[entry._row][entry._col]=2;//標記入口先為2 //棧尋找迷宮是否有出口 GetMazePath((int*)maze,N,entry,path); PrintMaze((int*)maze,N); cout<<"迷宮是否有通路?"<<!path.empty()<<endl; } int main() { TestMaze(); system("pause"); return 0; }
棧中儲存路徑:
二、遞迴實現迷宮最短路徑問題
問題描述:迷宮中有幾條通路路徑,用遞迴求解迷宮中的最短路徑,如下迷宮圖:
思想:
1.找到入口點,上下左右試探,試探時若下一個位置為0或者若下一個位置值大於原位置值加1,則表示為一個通路,若找到通路則遞迴呼叫自身,每走一次標記其值為前一個值加1,並將每個位置入棧儲存;
2.若找到通路將此時原棧中儲存的路徑儲存到另一個空棧ShortPath中,原棧則pop,並且遞迴結束返回;
3.如上圖返回到位置{6,7},其左邊為0為通路,在開始遞迴呼叫,呼叫到{6,6},值變為18,其左邊位置為0通路,呼叫到{6,5},值變為19,下一個位置值都不大於原位置值加1,在此遞迴結束原路返回;
4.再返回到位置{6,4},其右邊位置值大於{6,4}位置值加1,為通路,在此開始遞迴呼叫,每次下一個位置值都大於原位置值加1,再此找到第二條通路;
5.將此時棧與ShortPath的個數size比較,若小於,則在將此時棧內容賦於ShortPath中;
6.原棧再次pop,遞迴又一次結束原路返回,最終到入口點,原棧為空,ShortPath儲存路徑為迷宮最短路徑。
程式碼實現:
bool CheakIsAccess(int *maze,size_t n,Pos cur,Pos next) //判斷每走一次是否為通路(遞迴)
{
if((maze[next._row*n+next._col]==1)||(next._row<0||next._row>=n)||(next._col<0||next._col>=n))
{
return false;
}
if(maze[next._row*n+next._col]==0)
{
return true;
}
if(maze[next._row*n+next._col]>maze[cur._row*n+cur._col]+1)//若next位置值大於cur位置值加1則表示為一個新路
{
return true;
}
return false;
}
void GetMazePathR(int *maze,size_t n,Pos entry,stack<Pos> &path,stack<Pos> &ShortPath) //遞迴方法求解最短路徑
{
assert(maze);
Pos cur=entry;
path.push(cur);
Pos next=cur;
if(cur._row==n-1)
{
if(ShortPath.empty()||path.size()<ShortPath.size()) //將最短路徑給ShorytPath棧
{
ShortPath=path;
}
path.pop();
return;
}
//上
next=cur;
next._row-=1;
if(CheakIsAccess(maze,n,cur,next))
{
maze[next._row*n+next._col]=maze[cur._row*n+cur._col]+1; //每走一次將現值標記為前一個值加1
GetMazePathR((int*)maze,N,next,path,ShortPath);
}
//右
next=cur;
next._col+=1;
if(CheakIsAccess(maze,n,cur,next))
{
maze[next._row*n+next._col]=maze[cur._row*n+cur._col]+1;
GetMazePathR((int*)maze,N,next,path,ShortPath);
}
//下
next=cur;
next._row+=1;
if(CheakIsAccess(maze,n,cur,next))
{
maze[next._row*n+next._col]=maze[cur._row*n+cur._col]+1;
GetMazePathR((int*)maze,N,next,path,ShortPath);
}
//左
next=cur;
next._col-=1;
if(CheakIsAccess(maze,n,cur,next))
{
maze[next._row*n+next._col]=maze[cur._row*n+cur._col]+1;
GetMazePathR((int*)maze,N,entry,path,ShortPath);
}
path.pop();
}
void TestMaze()
{
int maze[N][N];
GetMaze((int*)maze,N);
PrintMaze((int*)maze,N); //列印迷宮
cout<<endl;
stack<Pos> path;
stack<Pos> ShortPath; //(遞迴實現中)儲存棧中最短路徑
Pos entry={2,0}; //入口
maze[entry._row][entry._col]=2;//標記入口先為2
//遞迴尋找迷宮最短路徑
GetMazePathR((int*)maze,N,entry,path,ShortPath);
PrintMaze((int*)maze,N);
cout<<"迷宮是否有通路?"<<!ShortPath.empty()<<endl;
}