一、实验内容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、程序完成对迷宫路径的搜索,为了更好地显示出求解结果,如果存在路径,则以长方形形式将迷宫打印出来,而不是只按步输出坐标,也便于检查路径的正确性,用特定符号标出迷宫的物理状态,其中字符“#”表示出口和入口,“<”标记出可行的路径;如果程序完成搜索后没有找到通路,则提示用户“No Path!”。
如图所示: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的下属单元二维数组[row+2][d+2]已存在,,也已作为下属存储单元存在操作结果:构成数据迷宫,用数值标识迷宫的物理状态,以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;⒊本程序模块结构⑴主函数模块void main(){初始化迷宫和栈;创建迷宫;输出迷宫;搜索路径;输出路径;}⑵栈模块——实现栈抽象数据类型;⑶迷宫模块——实现迷宫抽象数据类型;各模块之间的调用关系如下:主程序模块迷宫模块栈模块三、详细设计 1、基本数据类型操作⑴ 栈模块① typedef struct{int order;Position seat;int direction;。
56、为了便于找到最短路径,起初只使用了靠近出口处的五个方向,但是发现有特殊情况存在时由于不能想远离出口的方向行进而找不到路径,因此在搜索路径时进行了两次搜索,第一次使用五个靠进出口的方向搜索,找到路径时则返回SUCCESS ,若未搜索到则再进行一次八个方向的搜索,即为了防止漏掉特殊情况,找到时则返回SUCCESS ,由于第一搜索无结果若第二次搜索到路径也只能是特殊情况,故也应该是最短路径(之一)。
7、最大的问题是并没有按照题目要求来做,因为题目中要求用队列来存储路径,经过实验发现有很多问题难以解决,故选择了只用栈的方法来实现。
四、用户说明⒈ 本程序的运行环境为windows 7(64位)操作系统,执行文件为数据结构迷宫.exe ; ⒉ 进入演示程序后,即显示对话形式的提示操作过程,1、提出输入迷宫的大小2、按enter 键输出随机生成的迷宫3、按enter 键开始搜索路径搜索结果:The Path:输出迷宫及路径或者输出No Path!⒊提示输入迷宫大小后,用户输入所要处理迷宫的长row,宽col;⒋提示输入迷宫后,用户将迷宫输入,0代表可行,1代表障碍;⒌按任意键开始后,程序自动进行对所建迷宫的搜索,并将搜索结果;⒍按任意键退出程序。
五、测试结果1、无路径:2、找到最短路径:六、附录(源代码及注释)#include ""#include ""#include ""#include ""#define NULL 0#define TRUE 1#define FALSE 0#define SUCCESS 1#define FAIL 0#define OK 1#define ERROR 0#define OVERFLOW -1#define INFEASIBLE -2#define MAXLENGTH 50#define MAXWIDTH 50#define STACK_INIT_SIZE 100#define STACKINCRENT 10=row&&[num].==col)//路径某一步所在的位置return TRUE;num++;p++;}return FALSE;}Status MazePath(MAZE M,SqStack *Path){// 若迷宫maze中从入口start到出口end的通道,则求得一条存放在栈中// (从栈底到栈顶),并返回SUCCESS;否则返回FAILPosition curpos;int curstep;SElemType e,te;curpos=; // 设定"当前位置"为"入口位置"curstep=1; // 探索第一步//第一次查找路径,设置5个方向(不远离!终点的五个方向),若找到则返回SUCCESS do{if(Pass(M,curpos)){ // 当前位置可通过,即是未曾走到过的通道块[][]=' '; // 留下足迹=1;=curstep;=curpos;Push(S,&e); // 加入路径if==&&==// 到达终点(出口)return SUCCESS;}curpos=NextPos(curpos,1); // 下一位置在当前位置的右下方curstep++; // 探索下一步}else{ // 当前位置不能通过if(!StackEmpty(S)){Pop(S,&e);while==5&&!StackEmpty(S)){[][]=' '; //标记不能通过Pop(S,&e); // 留下不能通过的标记,并退回一步} // whileif<5){++;GetTop(S,&te);if==5&&==2){Pop(S,&e);++;}Push(S,&e); // 换下一个方向探索curpos=NextPos,; // 当前位置设为新方向的相邻块} // if} // if} // else}while(!StackEmpty(S));curpos=; // 设定"当前位置"为"入口位置"curstep=1; // 探索第一步//如果第一次查找无结果则再进行一次八个方向的查找,检查是否存在第一次查找不到的特殊情况do{if(Pass(M,curpos)){ // 当前位置可通过,即是未曾走到过的通道块[][]=' '; // 留下足迹=1;=curstep;=curpos;Push(S,&e); // 加入路径if==&&==// 到达终点(出口)//PrintPath(maze,S); //输出路径return SUCCESS;}curpos=NextPos(curpos,1); // 下一位置是当前位置的东邻curstep++; // 探索下一步}else{ // 当前位置不能通过if(!StackEmpty(S)){Pop(S,&e);while==8&&!StackEmpty(S)){[][]=' '; //标记不能通过Pop(S,&e); // 留下不能通过的标记,并退回一步} // whileif<8){++;GetTop(S,&te);if==4&&==2){Pop(S,&e);++;}Push(S,&e); // 换下一个方向探索curpos=NextPos,; // 当前位置设为新方向的相邻块} // if} // if} // else}while(!StackEmpty(S));return FAIL;} // MazePathvoid PrintPath(MAZE M, SqStack Path){int row,col;SElemType *p=;int num=0;if(StackEmpty(&Path)){printf("No Path!\n");//路径栈为空,则提示无路径exit(OVERFLOW);}printf("\nThe Path:\n");for(row=0;row<+2;row++){for(col=0;col<+2;col++){if(SameSeat(Path,row,col)){if((row==1&&col==1)||(row==&&col==)printf("# ");elseprintf("> ");num++;p++;}elseprintf("%c ",[row][col]);}printf("\n");}}//栈操作Status InitStack(SqStack *S)//初始化栈{S->base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));if(!S->base)exit(OVERFLOW);S->top=S->base;//空栈的栈顶和栈底位置相同S->stacksize=STACK_INIT_SIZE;return OK;}Status StackEmpty(SqStack *S){if(S->top==S->base)return TRUE;elsereturn FALSE;}Status Push(SqStack *S,SElemType *e)//在栈顶插入新的元素,入栈{if(S->top-S->base>=S->stacksize){S->base=(SElemType*)realloc(S->base,(S->stacksize+STACKINCRENT)*sizeof(SElemType));//重新分配空间if(!S->base)exit(OVERFLOW);S->top=S->base+S->stacksize;S->stacksize+=STACKINCRENT;}*S->top++=*e;return OK;}Status GetTop(SqStack *S,SElemType *e)//从栈中取元素{if(S->top==S->base)return ERROR;*e=*(S->top-1);return OK;}Status Pop(SqStack *S,SElemType *e){if(S->top==S->base)return ERROR;*e=*--S->top;return OK;}。