迷宫实验报告————————————————————————————————作者:————————————————————————————————日期:一、实验内容3、迷宫问题。
假设迷宫由m行n列构成,有一个出口和一个入口,入口坐标为(1,1),出口坐标为(m,n),试设计并验证以下算法:找出一条从入口通往出口的路径,或报告一个“无法通过”的信息。
(1)用C语言实现顺序存储队列上的基本操作,然后利用该队列的基本操作找出迷宫的一条最短路径。
(2)设计一个二维数组MAZE[m+2][n+2]表示迷宫,数组元素为0表示该位置可以通过,数组元素为1表示该位置不可以通行。
MAZE[1][1]、MAZE[m][n]分别为迷宫的入口和出口。
(3)输入迷宫的大小m行和n列,动态生成二维数组;由随机数产生0或1,建立迷宫,注意m*n的迷宫需要进行扩展,扩展部分的元素设置为1,相当于在迷宫周围布上一圈不准通过的墙。
(4)要求输出模拟迷宫的二维数组;若存在最短路径,则有出口回溯到入口(出队列并利用栈实现),再打印从入口到出口的这条路径,例如(1,1),......,(i,j),......,(m,n);若没有路径,则打印“No path”。
(5)迷宫的任一位置(i,j)上均有八个可移动的方向,用二维数组Direction存放八个方向的位置偏移量。
Direction[8][2]={{0,1},{1,1},{0,-1},{-1,-1},{1,-1},{1,0},{-1,0},{-1,1}};(6)为避免出现原地踏步的情况需要标志已经通过的位置,采用一个标志数组MARK[m+2][n+2],初值均为0,在寻找路径的过程中,若通过了位置(i,j),则将MARK[i][j]置为1。
(7)为了记录查找过程中到达位置(i,j)及首次到达(i,j)的前一位置(i_pre,j_pre),需要记住前一位置(i_pre,j_pre)在队列中的序号pre,即队列中数据元素应该是一个三元组(i,j,pre)。
(8)搜索过程简单下:将入口MAZE[1][1]作为第一个出发点,依次在八个方向上搜索可通行的位置,将可通行位置(i,j,pre)入队,形成第一层新的出发点,然后依次出队,即对第一层中各个位置分别搜索它所在八个方向上的可通行位置,形成第二层新的出发点,...,如此进行下去,直至达到出口MAZE[m][n]或者迷宫所有位置都搜索完毕为止。
二、实验过程及结果一、需求分析1、用栈的基本操作完成迷宫问题的求解,其中栈的基本操作作为一个独立的模块存在。
2、以二维数组M[m+2][n+2]表示迷宫,M[i][j] 表示迷宫中相应(i,j)位置的通行状态(0:表示可以通行,1:表示有墙,不可通行),完成迷宫的抽象数据类型,包括出口、入口位置等。
3、用户从屏幕上输入迷宫,从键盘输入迷宫的大小,即迷宫的长和宽(由于控制台大小限制,输入的长宽需在50以下),完成对应迷宫的初始化。
根据键盘输入的迷宫规格随机生成大小相同的迷宫,产生方块的地方为墙,无方块的地方可通过,如下例所示:如下所示:4、程序完成对迷宫路径的搜索,为了更好地显示出求解结果,如果存在路径,则以长方形形式将迷宫打印出来,而不是只按步输出坐标,也便于检查路径的正确性,用特定符号标出迷宫的物理状态,其中字符“#”表示出口和入口,“<”标记出可行的路径;如果程序完成搜索后没有找到通路,则提示用户“NoPath!”。
如图所示:5、程序执行的命令:⑴创建初始化迷宫;⑵搜索迷宫;⑶输出搜索到的最短路径。
二、概要设计(按照题目要求应该用队列实现路径的存储,但操作过程中遇到很多困难未能解决,故选择栈的操作来实现路径的存储)1、迷宫的抽象数据类型定义:ADT Maze{数据对象:D:={aij,Start,end|-20<=aij<20,Start,end∈{(i,j)|0≤i≤m+2,0≤j≤n+2,m,n≥0}}数据关系:R={length,width}ﻩﻩlength={<ai-1j,aij>|ai-1,aij∈D i=1,…,m+2,j=1,…,n+2}width={<aij-1,aij>|aijaij-1∈D}基本操作:SetMaze(&M)初始条件:M已经定义,M的下属单元二维数组M.Maze[row+2][d+2]已存在,M.start,M.end也已作为下属存储单元存在操作结果:构成数据迷宫,用数值标识迷宫的物理状态,以0表示通路,以1表示障碍,由终端读取迷宫空间大小,各点处的具体物理状态及Start和End点位置,完成迷宫构建Pass(M, CurPos)初始条件:已知目前迷宫状态及当前位置操作结果:完成相应的搜索任务,如果可行,则返回1NextPos(CurPos,directionr)操作结果:返回CurPOS位置在方向direction上的下一位置SameSeat(Path,row,col)操作结果:若(row,col)位置是路径Path中的一个点,则返回TRUEPrintMaze(M)初始条件:迷宫M已存在操作结果:输出字符标示的迷宫MazePath(M,&Path)初始条件:迷宫M已存在操作结果:搜索迷宫,用path返回搜索所得路径。
如不存在,返回0PrintPath(M,Path)初始条件:迷宫M已存在操作结果:迷宫M存在可行路径则将迷宫M及相应最短路径一起打印输出}ADT MAZE;⒊本程序模块结构⑴主函数模块voidmain(){初始化迷宫和栈;创建迷宫;输出迷宫;搜索路径;输出路径;}⑵栈模块——实现栈抽象数据类型;⑶迷宫模块——实现迷宫抽象数据类型;各模块之间的调用关系如下:ﻩ主程序模块迷宫模块栈模块三、详细设计1、基本数据类型操作⑴栈模块① typedef struct{intorder;Positionseat;ﻩint direction;}SElemType;//步数、方向及位置//栈定义②typedefstruct lnode{ﻩSElemType *base;ﻩSElemType*top;//设两指针便于判断栈是否为空ﻩintstacksize;//栈当前可使用的最大容量}SqStack;③参数设置:#define STACK_INIT_SIZE 100#define STACKINCRENT10//----------基本操作的算法描述--------------------Status InitStack(SqStack &s){ //构造一个空栈S.base=(SelemType )malloc(STACK_INIT_SIZE*SizeOf(SelemType));if(!S.base)exit(OVERLOW); //存储分配失败S.top=S.base;S.stacksize=STACK_INIT_SIZE;return ok;}StatusStackEmpty(Sqstack&S){// 若S为空返回TRUE,否则返回FALSEreturnS.base==S.top;}StatusGetTop(SqStack &S,Selemtype &e){//栈不空,用e返回s的栈顶元素及OK,否则返回ERRORif(S.top==S.base)returnERROR;e=*(S.top-1);returnok;}StatusPush(Sqstack &S,SelemType &e){ //插入元素e为新的栈顶元素if(S.top-S.base>=S.stacksize){//栈满追加存储空间S.base=(SelemType)realloc(S.base,(S.stacksize+STACKICREMENT)Sizeof(Selemtype));if(!S.base) exit(OVERFLOW)// 存储分配失败S.top=S.base+S.stacksize; //确定新的栈顶指针S.stacksize+=STACKINCREMENT;// 已分配空间增加}*S.top++=*e;return ok;}Status Pop(Sqstack &s,SelemType&e){//若栈不变,则删除栈顶元素,用e返回其值及ok,否则falseif(S.top=o=S.base)returnERROR;*e=*--S.top; // 顶指针减小,用e返回其数据returnok;}⑵迷宫模块:①迷宫的数据类型#defineMAXLENGTH 50 //迷宫的最大长度#defineMAXWIDTH50 //屏幕宽度,迷宫的最大宽度//元素类型typedefintStatus;typedef struct{ﻩintrow;int col;}Position; //位置坐标//迷宫定义typedef int ElemType;typedef struct MAZE{ElemType Maze[MAXLENGTH][MAXWIDTH]; // 迷宫的物理状态描述ﻩintlength,width;ﻩ//迷宫的大小ﻩPosition start,end;ﻩ//开始与结束位置与栈的单元类型相同}MAZE;ﻩ//“迷宫”型数据②迷宫模块中的基本操作Status semaze(MAZE &M){Printf(“Please input the length andwidthof the MAZE”);sanf(“rlength,width”);for(k=0;k<width+2;k++)ﻩﻩM->Maze[0][k]=1;ﻩfor(j=0;j<length+2;j++)ﻩﻩM->Maze[j][0]=1;ﻩfor(j=0;j<length+2;j++)ﻩﻩM->Maze[j][width+1]=1;for(k=0;k<width+2;k++)ﻩﻩM->Maze[length+1][k]=1; //设置迷宫围墙for(j=1;j<=length;j++){ﻩfor(k=1;k<=width;k++)M->Maze[j][k]=rand()%2;ﻩ//随机生成迷宫ﻩ}M->length=length;ﻩM->width=width;M->start.row=1;M->start.col=1;M->end.row=M->length;M->end.col=M->width;ﻩﻩM->Maze[M->start.row][M->start.col]=M->Maze[M->end.row][M->end.col]=0;//入口和出口设置*/}void PrintMaze(MAZE M){// 打印出迷宫,包括边界printf("迷宫入口:[1,1]--用#表示\n");ﻩprintf("迷宫出口:[%d,%d]--用#表示\n",M.length,M.width);ﻩfor(row=0;row<M.length+2;row++){ﻩﻩfor(col=0;col<M.width+2;col++){ﻩﻩﻩif((row==1&&col==1)||(row==M.length&&col==M.width))printf("#"); //入口和出口用#表示elseﻩprintf("%c",M.Maze[row][col]);}ﻩprintf("\n");}// 用字符型打印输出(i,j)状态}Status Pass(MAZE M,PositionCurPos){//判断迷宫中当前位置CurPos上能否通过// 如果通过返回1if(M.Maze[CurPos.row][CurPos.col]==0)return1;// 如果当前位置是可以通过,返回1else return 0; // 其它情况返回0}Position NextPos(Position CurPos, int direction){//返回当前位置在direction方向上的下一位置ﻩReturnPos.row=CurPos.row+Direction[direction-1][0];ReturnPos.col=CurPos.col+Direction[direction-1][1];return ReturnPos;}Status SameSeat(SqStack Path,int row,int col){//位置(row,col)在路径中时返回TRUEﻩwhile(p<Path.top){ﻩﻩif(Path.base[num].seat.row==row&&Path.base[num].seat.col==col)//路径某一步所在的位置ﻩreturn TRUE;num++;ﻩp++;ﻩ}return FALSE;}Status MazePath(MAZE M,SqStack *S){// 若迷宫maze中从入口start到出口end的通道,则求得一条存放在栈中// (从栈底到栈顶),并返回SUCCESS;否则返回FAILcurpos=M.start; // 设定"当前位置"为"入口位置"curstep=1; //探索第一步//第一次查找路径,设置5个方向(不远离!终点的五个方向),若找到则返回SUC CESSﻩdo{if(Pass(M,curpos)){// 当前位置可通过,即是未曾走到过的通道块ﻩﻩM.Maze[curpos.row][curpos.col]=''; // 留下足迹ﻩe.direction=1;ﻩe.order=curstep;ﻩﻩ e.seat=curpos;Push(S,&e);// 加入路径if(curpos.row==M.end.row&&curpos.col==M.end.col){ ﻩﻩ//到达终点(出口)ﻩreturn SUCCESS;ﻩ}ﻩcurpos=NextPos(curpos,1);//下一位置在当前位置的右下方ﻩcurstep++; //探索下一步}ﻩelse{// 当前位置不能通过ﻩif(!StackEmpty(S)){ﻩﻩPop(S,&e);ﻩﻩwhile(e.direction==5&&!StackEmpty(S)){ﻩﻩM.Maze[curpos.row][curpos.col]=''; //标记不能通过Pop(S,&e); //留下不能通过的标记,并退回一步ﻩ}// whileﻩif(e.direction<5){ﻩ e.direction++;ﻩﻩGetTop(S,&te);ﻩif(e.direction==5&&te.direction==2){ﻩﻩPop(S,&e);ﻩﻩ e.direction++;ﻩ}ﻩﻩﻩﻩPush(S,&e); //换下一个方向探索ﻩﻩcurpos=NextPos(e.seat,e.direction); //当前位置设为新方向的相邻块ﻩﻩﻩ} //ifﻩ} //ifﻩ}//else}while(!StackEmpty(S));ﻩcurpos=M.start; // 设定"当前位置"为"入口位置"curstep=1; // 探索第一步ﻩ//如果第一次查找无结果则再进行一次八个方向的查找,检查是否存在第一次查找不到的特殊情况ﻩdo{ﻩif(Pass(M,curpos)){ // 当前位置可通过,即是未曾走到过的通道块ﻩﻩM.Maze[curpos.row][curpos.col]='';//留下足迹ﻩe.direction=1;ﻩ e.order=curstep;ﻩﻩ e.seat=curpos;ﻩPush(S,&e);//加入路径if(curpos.row==M.end.row&&curpos.col==M.end.col){ﻩ// 到达终点(出口)ﻩﻩ//PrintPath(maze,S); //输出路径ﻩﻩreturn SUCCESS;}ﻩﻩcurpos=NextPos(curpos,1); //下一位置是当前位置的东邻ﻩcurstep++;// 探索下一步}ﻩelse{ //当前位置不能通过ﻩﻩif(!StackEmpty(S)){Pop(S,&e);ﻩwhile(e.direction==8&&!StackEmpty(S)){ﻩﻩM.Maze[curpos.row][curpos.col]='';ﻩ//标记不能通过ﻩﻩPop(S,&e);// 留下不能通过的标记,并退回一步ﻩ} // whileﻩif(e.direction<8){ﻩﻩ e.direction++;ﻩGetTop(S,&te);ﻩﻩﻩif(e.direction==4&&te.direction==2){ﻩﻩﻩﻩPop(S,&e);ﻩﻩ e.direction++;ﻩ}ﻩﻩﻩﻩPush(S,&e); //换下一个方向探索ﻩcurpos=NextPos(e.seat,e.direction);// 当前位置设为新方向的相邻块ﻩﻩ}//if}//if} // else}while(!StackEmpty(S));ﻩreturn FAIL;}//MazePathvoid PrintPath(MAZEM,SqStackPath){// 将迷宫及路径一起打印if(StackEmpty(&Path)){ﻩﻩprintf("NoPath!\n");//路径栈为空,则提示无路径ﻩexit(OVERFLOW);}ﻩprintf("\nThe Path:\n");for(row=0;row<M.length+2;row++){for(col=0;col<M.width+2;col++){ﻩif(SameSeat(Path,row,col)){ﻩﻩif((row==1&&col==1)||(row==M.length&&col==M.width)) ﻩﻩﻩﻩprintf("# ");ﻩﻩelseﻩﻩﻩprintf(">");ﻩﻩnum++;ﻩp++;ﻩﻩﻩ}ﻩﻩelseﻩﻩprintf("%c ",M.Maze[row][col]);ﻩ}ﻩﻩprintf("\n");ﻩ}}⑶ 主函数算法:void m ain(){ﻩMA ZE M;Sq Stack path ;ﻩInit Sta ck(&p ath);SetMaz e(&M);ﻩPr intMaze (M);Maze Path(M,&path);Prin tPat h(M,p ath);}⒉ 函数的调用关系反映了本演示程序的层次结构 ma inInit Stac k Maz ePa th P rintP ath Pri ntMaze Pass SetMaze Sa meSe at N ex tP osﻩ……Push G etTop P op StackEmp ty四、调试分析1、开始没有将M[n ][m].sta rt.end 设置为MAZ E 型数据的下属单元,使得各个迷宫操作的函数参数十分散杂,调试时各参数关系不易把握。