数据结构小组课题——迷宫
- 格式:doc
- 大小:104.50 KB
- 文档页数:17
数据结构——迷宫求解迷宫问题最早出现在古希腊神话中。
据说,半人半兽的英雄西修斯在克里特的迷宫中勇敢地杀死半人半牛的怪物,并循着绳索逃出迷宫。
希腊史学家希罗多德曾探访过那里。
他描述说,整个迷宫由12座带顶院落构成,所有的院落都由通道连接,形成3000个独立的“室”。
后来的参观者也说,一旦进入迷宫,如果没有向导,根本无望走出。
历史上,人们认为迷宫具有魔力。
后来,迷宫成为游戏。
在如今计算机非常普及的情况下,迷宫又以游戏程序的形式呈现在我们日常使用的电脑上。
解题思路解析:在迷宫问题中,一个迷宫用二维数组maze[size] [size]来存储,对当前位置(探索过程中某一时刻所在的位置)记为maze[curpos.row][curpos.line],如果maze[curpos.row][curpos.line]=0则有通路,如果maze[curpos.row][curpos.line]=1则无通路。
从入口star出发,可沿四个方向前进。
试探方向的变化0表示E、1表示S、2表示W、3表示N。
如果遇到“0”则可前进,遇“1”则受阻;按此规则求入口到某一确定出口的一条路径。
算法的基本思想是:从迷宫的入口出发,进行判断,若当前位置可通(未走过的“0”),则将当前位置插入栈顶,然后判断此位置是否为出口位置,如果是则结束整个运算,若不是出口位置,改变当前位置到他的右临位置(即E方向);否则如果当前位置不通,这时若栈不空且栈顶位置尚有其他位置未搜索,则设定新的当前位置为栈顶位置的下一邻块,若栈不空且栈顶位置四周不通,则删去栈顶位置,若栈不空,则重新测试新的栈顶位置,直到找到一个可通的邻块或栈空,如此重复判断,直到找到一条从入口到出口的一条路径或得到无路径的信息。
一.需求分析问题描述:以一个size*size的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。
设计一个程序,对任意设定输入的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。
a、输入的形式和输入值的范围:以0,1输入表示迷宫,0为通路,1为障碍。
向电脑输入由0和1组成的二维数组,数组的行号列号相同且都为size(1=<size<=10)。
b、输出的形式:首先输出迷宫“#”表示墙壁,“0”表示通道,“1”表示障碍。
然后输出走出迷宫的路径,“#”表示墙壁,“0”表示迷宫通路,由于时间仓促不免会有些瑕疵,当好多“0”紧挨在一起的时候,有顺时针方向表示迷宫通道(由右方开始)。
c、程序所能达到的功能:实现输出迷宫通道,当迷宫无通道时显示“找不到通路”。
d、测试数据:由后面调试分析详细说明。
二.概要设计1.数据结构的定义为实现输出迷宫路径需用到栈,下面定义栈的抽象数据类型:栈(stack)是限定仅在表尾进行插入或删除操作的线性表。
因此,对栈来说,表尾端有特殊含义,称为栈顶(top),相应的,表头端称为栈底(bottom)。
不含元素的空表称为空栈。
假设栈S=(a1,a2,…,an),则称a1为栈底元素,an为栈顶元素。
栈的修改是按后进先出的原则进行的。
因此,栈又称为后进先出(List In First Out)的线性表。
(简称LIFO结构)ADT Stack{数据对象:D={ai|ai∈ElemSet,i=1,2,…,n, n>=0}数据关系:R1={<ai-1,ai>|ai-1,ai∈D,i=2,…,n,}约定an端为栈顶,a1端为栈底。
在迷宫问题中,假设以栈S记录“当前路径”,则栈顶中存放的是“当前路径上最后一个通道块”。
由此,“纳入可以走的路径”的操作即为“当前位置入栈”;“从当前路径上删除前一通道块”的操作即为“出栈”。
2.程序模块(1)迷宫和栈的定义:typedef enum { ERROR, OK } Status;typedef struct{int row; //row表示“行”号int line; //line表示“列”号}PosType; //位置的元素类型typedef struct{int ord; //该通道在路径上的“序号”PosType seat; //通道块在迷宫中的“坐标位置”int di; //从此通道走向下以通道块的“方向”}SElemType; //栈的元素类型typedef struct{SElemType * base; //在构造之前和销毁之后,base的值为NULLSElemType * top; //栈顶指针int stacksize; //当前已分配的存储空间}SqStack;(2)构造一个空栈S:Status InitStack(SqStack &S){S.base=(SElemType *)malloc(100*sizeof(SElemType));if(!S.base)return ERROR; //存储分配失败S.top=S.base;S.stacksize=100;return OK;}(3)进栈:Status Push(SqStack &S,SElemType &e){ //插入元素e为新的栈顶元素if (S.top-S.base>=S.stacksize){ //栈满,追加存储空间S.base=(ElemType*) realloc (S.base,(S.stacksize+STACKINCREMENT)*sizeof (ElemType));If(!S.base) exit (overflow) //存储分配失败S.top=S.base+S.stacksize;S.stacksize+= STACKINCREMENT;}*S.top++=e;Return OK;}由于本题中在为栈分配存储空间的时候S.stacksize=100,为迷宫最大的元素数,因此不存在栈满的情况。
(4)删除栈顶元素:Status Pop(SqStack &S,SElemType &a){ //若栈不空,则删除S的栈顶元素,用a返回其值,并返回OK,否则返回ERROR if(S.top==S.base)return ERROR;a=*--S.top;return OK;}(5)判栈空:Status StackEmpty(SqStack S){if(S.top==S.base) return OK;return ERROR;}3、各模块之间的调用关系:(1)本程序主要包括以下函数:(1)主函数main()(2)初始化迷宫函数Initmaze()(3)显示所创建的迷宫函数printmaze ()(4)进入下一个位置函数NextPos()(5)创建空栈函数InitStack()(6)检查是否为空栈函数StackEmpty()(7)插入栈元素函数push ()(8)删除栈元素函数pop ()(9) 寻找到路径函数mazepath ()(10) 输出路径函数printpath()(11)标记当前位置函数Markfoot()(12)判断当前位置是否可通函数Pass()(2)各种函数之间的调用关系如下:三.详细设计:解决迷宫问题最重要的程序段是找到通路,并存储在栈里,现只分析实现这一过程的函数MazePath(),其余调用函数在附录里有详细说明:do {若当前位置可通,则{ 将当前位置插入栈顶;若该位置是出口位置,则结束;否则切换当前位置的右邻方块为新的当前位置;}否则,若栈不空且栈顶位置尚有其他方向未经探索,则设定的新的当前位置为沿顺时针方向旋转找到的栈顶位置的下一相邻块;若栈不空但栈顶位置的四周均不可通,则{ 删去栈顶位置;若栈不空,则重新测试新的栈顶位置,直到找到一个可通的相邻块或出栈至栈空;}}while(栈不空);在此,尚需说明一点的是,所谓当前位置可通,指的是未曾走到过的通道块,即要求该方块位置不仅是通道块,而且既不在当前路径上(否则所求路径就不是简单路径),也不是曾经纳入过路径的通道块(否则只能在死胡同内转圈)。
Status MazePath (MazeType maze,PosType start, PsoType end) {//若迷宫maze中存在从入口start到出口end的通道,则求得一条存放在栈中(从栈底到//栈顶),并返回TURE;否则返回FALSEInitStack(S); curpos=start; //设定“当前位置”位“入口位置”curstep=1; //探索第一步do{if (Pass(curpos)) { //当前位置可以通过,即是未曾走到过的通道块FootPrint(curpos); //留下足迹e=(curstep,curpos,1);Push(S,e); //加入路径if (curpos==end) return (TRUE); //到达终点(出口)curpos=NextPos(curpos,1); //下一位置是当前位置的右邻curstep++; //探索下一步}else { //ifif (!StackEmpty(S)) { //当前位置不能通过Pop (S,e);while (e.di==3 && !StackEmpty(S)) {MarkPrint(e.seat); Pop(S,e); //留下不能通过的标记,并退回一步}if (e.di<3) {e.di++; Push (S,e); //换下一个方向探索curpos=NextPos (e.seat e.di); //设定当前位置是该新方向上的相邻块} //if} //if} //else}while (!StackEmpty(S));return (FALSE);} //MazePath四.调试分析:a、调试过程中遇到的问题是如何解决的以及对设计与实现的回顾讨论和分析:遇到的问题:(1)首先是对实现迷宫由当前方向转向下一方向时应当怎样实现遇到了困难,具体解析见解题思路。
(2)然后是当前位置不可通,即当前位置的四周方向都试探过后发现不是通路,如何实现退向后一位置。
如果当前位置不通,则留下足迹即把当前位置标记为1,再把当前位置从栈顶位置删除,这样此位置就不会循环往复的再次被走到。
(3)如何实现输出迷宫通路的问题。
当当前位置坐标和迷宫终点坐标相同时,即迷宫走到出口。
这时迷宫里所有元素还是由“0”和“1”组成,把栈中元素标记为“2”以示和别的元素区别。
把数组中所有为“2”的元素输出“0”,不是2的元素输出“”,围墙由“#”输出。
这样就解决了迷宫路径的输出问题。
在这个问题上由于输入数据的时候是输入一个0或1,然后再输入一个空格,这样会比较美观。
但是最初想到的是用“”即一个空格代表不是通路的元素,这样最终输出的时候就出现问题了,迷宫的最右端围墙“#”会不在最后一列出现,输出错误的通道或者就显示不出通道。