问题描述:给定一个M X N的迷宫图,求一条从指定入口的到出口的路径
数据组织:为了表示迷宫,设置一个二维数组(migong) mg[ M][N] ,其中数组的每个元素表示一个方块的状态,0表示对应的方块为通道,1表示对应方块是墙。为了算法方便,在迷宫外面加一道墙,所以二维数组表示演变如下:(假设M=8,N=8)
mg[M+2][N+2]=
{ {1,1,1,1,1,1,1,1,1,1},
};{1,0,0,1,0,0,0,1,0,1}, {1,0,0,1,0,0,0,1,0,1}, {1,0,0,0,0,1,1,0,0,1}, {1,0,1,1,1,0,0,0,0,1}, {1,0,0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,1,0,0,1}, {1,0,1,1,1,0,1,1,0,1}, {1,1,0,0,0,0,0,0,0,1}, {1,1,1,1,1,1,1,1,1,1}
算法思路:
针对这类问题·,我们通常采用的是“”穷举法”,即从入口出发,顺某一方向向前试探,若能走通,则继续机组往前走,否则沿原路退回,换另一个方向在试探,直至把所有的路走完为止。为了保证在位置上都能沿原路退回(成为回溯),需要用一个后进先出栈来保存入口到当前位置的路径。为了保证试探的可走相邻方块不是已走路径上的方块,如(i,j)进栈,试探到(i+1,j),接着试探到(i,j),这样就是引起死循环,为此,在一个方块进栈后,将对应mg数组元素值改为-1,表示该方块为不可走相邻方块,当该方块退栈时,将其恢复为0
下面代码是给出找到一条路径的算法:
#include <iostream> using namespace std; #define MaxSize 100 #define M 8 //行数 #define N 8 //列数 int mg[M+2][N+2] = { {1,1,1,1,1,1,1,1,1,1}, {1,0,0,1,0,0,0,1,0,1}, {1,0,0,1,0,0,0,1,0,1}, {1,0,0,0,0,1,1,0,0,1}, {1,0,1,1,1,0,0,0,0,1}, {1,0,0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,1,0,0,1}, {1,0,1,1,1,0,1,1,0,1}, {1,1,0,0,0,0,0,0,0,1}, {1,1,1,1,1,1,1,1,1,1} }; typedef struct { int row; //当前方块的行号 int column; //当前方块的列号 int direction ; //下一个可走的方格的方位编号 0:上 1:右 2:下 3:左 }Box; //定义方块类型 typedef struct { Box data[MaxSize] ; int top ; //栈顶指针 }StackType ; //定义顺序栈类型 bool mgpath(int startRow,int startColumn,int endRow,int endColumn) { int i,j,k,direction,find; StackType stackType ; stackType.top = -1 ; stackType.top++; stackType.data[stackType.top].row = startRow ; stackType.data[stackType.top].column=startColumn; stackType.data[stackType.top].direction = -1 ; mg[startRow][startColumn]= -1 ; //栈不为空时循环 while(stackType.top > -1) { i=stackType.data[stackType.top].row; j=stackType.data[stackType.top].column; direction =stackType.data[stackType.top].direction; //取栈顶方块 if(i==endRow && j==endColumn) { cout<<"迷宫路径如下"<<endl; for(k=0;k<=stackType.top;k++) { cout<<"("<<stackType.data[k].row<<","<<stackType.data[k].column<<") " ; if((k+1)%5 == 0) cout<<endl; } cout<<endl; return true ; } find = 0 ; //找下一个可走的方块 while(direction < 4 && find==0) { direction++; switch(direction) { case 0 : i=stackType.data[stackType.top].row-1; j=stackType.data[stackType.top].column; break; case 1 : i=stackType.data[stackType.top].row; j=stackType.data[stackType.top].column+1; break; case 2: i=stackType.data[stackType.top].row+1; j=stackType.data[stackType.top].column; break; case 3: i=stackType.data[stackType.top].row; j=stackType.data[stackType.top].column-1; break; } if(mg[i][j] == 0) find = 1 ; //找到下一个可走的相邻方块 } //找到下一个可走方块 if(find == 1) { stackType.data[stackType.top].direction=direction ; stackType.top++; stackType.data[stackType.top].row=i ; stackType.data[stackType.top].column=j ; stackType.data[stackType.top].direction = -1 ; mg[i][j]=-1; } else { //没有路径可走,则退栈 mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ; stackType.top--; } } return false; } int main() { if(!mgpath(1,1,M,N)) cout<<"该迷宫问题没有解!"<<endl; cout<<"结束"<<endl; return 0 ; }
运行结果如下:
拓展:输出所有能从入口到出口的的路径和其中最短路径
代码如下:
#include <iostream> using namespace std; #define MaxSize 100 #define M 8 //行数 #define N 8 //列数 int mg[M+2][N+2] = { {1,1,1,1,1,1,1,1,1,1}, {1,0,0,1,0,0,0,1,0,1}, {1,0,0,1,0,0,0,1,0,1}, {1,0,0,0,0,1,1,0,0,1}, {1,0,1,1,1,0,0,0,0,1}, {1,0,0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,1,0,0,1}, {1,0,1,1,1,0,1,1,0,1}, {1,1,0,0,0,0,0,0,0,1}, {1,1,1,1,1,1,1,1,1,1} }; typedef struct { int row; //当前方块的行号 int column; //当前方块的列号 int direction ; //下一个可走的方格的方位位编号 0:上 1:右 2:下 3:左 }Box; //定义方块类型 typedef struct { Box data[MaxSize] ; int top ; //栈顶指针 }StackType ; //定义顺序栈类型 void mgpath(int startRow,int startColumn,int endRow,int endColumn) { int i,j,k,direction,find; int count=1; //路径数计数 int minlen=MaxSize; //最短路径长度 StackType path; StackType stackType ; stackType.top = -1 ; stackType.top++; stackType.data[stackType.top].row = startRow ; stackType.data[stackType.top].column=startColumn; stackType.data[stackType.top].direction = -1 ; mg[startRow][startColumn]= -1 ; //栈不为空时循环 while(stackType.top > -1) { i=stackType.data[stackType.top].row; j=stackType.data[stackType.top].column; direction =stackType.data[stackType.top].direction; //取栈顶方块 if(i==endRow && j==endColumn) { count++; //路径树基数 cout<<"迷宫路径如下"<<endl; for(k=0;k<=stackType.top;k++) { cout<<"("<<stackType.data[k].row<<","<<stackType.data[k].column<<") " ; if((k+1)%5 == 0) cout<<endl; } cout<<endl<<endl; //因为空栈top=-1,当栈里面有元素是为top=0,1,2,3,4...MaxSize-1 ,总共有MaxSize个元素 //所以stackType.top+1为栈里面的元素个数 if(stackType.top+1<minlen) { //比较输出最短路径 for(k=0;k<=stackType.top;k++) { path.data[k]= stackType.data[k]; } minlen=stackType.top+1; } //让该位置变为其他路径的可走结点 mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ; stackType.top--; i=stackType.data[stackType.top].row; j=stackType.data[stackType.top].column; direction= stackType.data[stackType.top].direction; } find = 0 ; //找下一个可走的方块 while(direction < 4 && find==0) { direction++; switch(direction) { case 0 : i=stackType.data[stackType.top].row-1; j=stackType.data[stackType.top].column; break; case 1 : i=stackType.data[stackType.top].row; j=stackType.data[stackType.top].column+1; break; case 2: i=stackType.data[stackType.top].row+1; j=stackType.data[stackType.top].column; break; case 3: i=stackType.data[stackType.top].row; j=stackType.data[stackType.top].column-1; break; } if(mg[i][j] == 0) find = 1 ; //找到下一个可走的相邻方块 } //找到下一个可走方块 if(find == 1) { stackType.data[stackType.top].direction=direction ; stackType.top++; stackType.data[stackType.top].row=i ; stackType.data[stackType.top].column=j ; stackType.data[stackType.top].direction = -1 ; mg[i][j]=-1; } else { //没有路径可走,则退栈 mg[stackType.data[stackType.top].row][stackType.data[stackType.top].column] = 0 ; stackType.top--; } } cout<<"最短路径如下:"<<endl; cout<<"长度:"<<minlen<<endl; cout<<"路径:"<<endl; for(k=0;k<minlen;k++){ cout<<"("<<path.data[k].row<<","<<path.data[k].column<<") " ; if((k+1)%5==0) //输出时每5个结点换一行 cout<<endl; } cout<<endl; } int main() { mgpath(1,1,M,N); cout<<"结束"<<endl; return 0 ; }
程序运行结果:
........................................................................................
.......................................................................................
.......................................................................................
读者可能会奇怪,为什么就能确定遍历完所有路径?
大家仔细想想,我每个方块按照(不包括边框的方块墙,我们自己加上去的)按照上、右、下、左的方向进行遍历(代码中direction代表方向),也就是说,所走的方块上下左右我都遍历完了。假设我们可以走的方块数目为W个,我们实际上遍历了4的W次方条可能路径,在进行剪枝操作,选取可行的路径。
第一个可行方块的遍历顺序为,上,右,下,左。如果第一个可行方块的上边有可行方块,我们将选取1第一方块的上面方块做为第二可行方块;如果第一方块上边没有可行方块,将找第一方块的右方块,如果是可行方块,将第一方块的有方块做为第二行方块,;如果第一方块的上,右没有可能方块,将遍历第一方块的下方块,如果还不是可行方块,将遍历第一方块的左方块,以此找到第二方块,第三方块.....
(此思路和递归雷同,相同的案例有:八皇后)