1、1课 程 设 计 报 告课程名称 数据结构课程设计 课题名称 迷宫问题 专 业 班 级 学 号 姓 名 指导教师 2012 年 6 月 9 日2课程设计任务书课程名称 数据结构课程设计 课 题 迷宫问题 专业班级 学生姓名 学 号 指导老师 审 批 任务书下达日期: 2012 年 6 月 9 日任 务 完 成 日 期 : 2012 年 6 月 16 日3一 、 设 计 内 容 与 设 计 要 求1设计内容:1)问题描述以一个 M*N 的长方阵表示迷宫,0 和 1 分别表示迷宫中的通路和墙壁。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出米有通路的结论。2)基本要求a.实现一
2、个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。求得的通路以三元组(i,j,d)的形式输出,其中:(i,j )指示迷宫中的一个坐标,d 表示走到下一个坐标的方向。b.编写递归形式的算法,求得迷宫中所有可能的通路。3)测试数据迷宫的测试数据如下:左上角(1,1)为入口,右下角(8,9)为出口。0 0 1 0 0 0 1 00 0 1 0 0 0 1 00 0 0 0 1 1 0 10 1 1 1 0 0 1 00 0 0 1 0 0 0 00 1 0 0 0 1 0 10 1 1 1 1 0 0 11 1 0 0 0 1 0 11 1 0 0 0 0 0 04)实现提示计算机解迷宫
3、通常用的是“穷举求解”方法,即从入口出发,顺着某一个方向进行探索,若能走通,则继续往前进;否则,沿着原路退回,换一个方向继续探索,直至出口位置,求得一条通路。假如所有可能的通路都探索到而未能到达出口,则设定的迷宫没有通路。可以二维数组存储迷宫数据,通常设定入口点的下标为(1,1) ,出口点的下标为(m,n) 。为处理方便起见,可在迷宫的四周加一圈障碍。对于迷宫中任一位置,均可约定有东、南、西、北四个方向可通。42设计要求: 课程设计报告规范1)需求分析a.程序的功能。b.输入输出的要求。2)概要设计a.程序由哪些模块组成以及模块之间的层次结构、各模块的调用关系;每个模块的功能。b.课题涉及的数
4、据结构和数据库结构;即要存储什么数据,这些数据是什么样的结构,它们之间有什么关系等。3)详细设计a.采用 C 语言定义相关的数据类型。b.写出各模块的类 C 码算法。c.画出各函数的调用关系图、主要函数的流程图。4)调试分析以及设计体会a.测试数据:准备典型的测试数据和测试方案,包括正确的输入及输出结果和含有错误的输入及输出结果。b.程序调试中遇到的问题以及解决问题的方法。c.课程设计过程经验教训、心得体会。5)使用说明用户使用手册:说明如何使用你编写的程序,详细列出每一步的操作步骤。6)书写格式见附带说明。7)附录a.参考书目b.源程序清单(带注释) 考核方式指导老师负责验收程序的运行结果,
5、并结合学生的工作态度、实际动手能力、创新精神和设计报告等进行综合考评,并按优秀、良好、中等、及格和不及格五个等级给出每位同学的课程设计成绩。具体考核标准包含以下几个部分: 平时出勤 (占 10%) 系统需求分析、功能设计、数据结构设计及程序总体结构合理与否(占 10%)5 程序能否完整、准确地运行,个人能否独立、熟练地调试程序(占 40%) 设计报告(占 30%)注意:不得抄袭他人的报告(或给他人抄袭) ,一旦发现,成绩为零分。 独立完成情况(占 10%) 。 课程验收要求 运行所设计的系统。 回答有关问题。 提交课程设计报告纸质稿。 提交源程序、设计报告文档电子稿。 依内容的创新程度,完善程
6、序情况及对程序讲解情况打分。二、进 度 安 排附:课程设计报告装订顺序:封面、任务书、目录、正文、评分、附件(A4 大小的图纸及程序清单) 。 正文的格式:一级标题用 3 号黑体,二级标题用四号宋体加粗,正文用小四号宋体;行距为 22。正文的内容:一、课题的主要功能;二、课题的功能模块的划分(要求画出模块图) ;三、主要功能的实现(至少要有一个主要模块的流程图) ;四、程序调试;五、总结;六、附件(所有程序的原代码,要求对程序写出必要的注释) 。正文总字数要求在 5000 字以上(不含程序原代码) 。6目录一、 任务书2二、 基本算法7三、 需求分析7a. 程序的功能7b. 输入输出的要求7c
7、. 程序算法分析8四、 概要设计8i. 设计中非递归程序的模块结构图8ii. 程序的数据结构和数据库结构分析9iii. 试探方向的设计10iv. 达某点,以避免发生死循环11五、 详细设计11a. 伪码设计11b. mgpath()流程图12六、 调试分析13七、 总结14八、 评分表16九、 附录(源代码清单)177一、基本算法走迷宫的过程可以模拟为一个搜索的过程:每到一处,总让它按东、东南、南、西南、西、西北、北、东北 8 个方向顺序试探下一个位置;如果某方向可以通过,并且不曾到达,则前进一步,在新位置上继续进行搜索;如果 8 个方向都走不通或曾经到达过,则退回一步,在原来的位置上继续试探
8、下一位置。每前进或后退一步,都要进行判断:若前进到了出口处,则说明找到了一条通路;若退回到了入口处,则说明不存在通路。用一个字符类型的二维数组表示迷宫,数组中每个元素取值“0” (表示通路)或“1”(表示墙壁) 。迷宫的入口点在位置(1,1)处,出口点在位置(m,m)处。设计一个模拟走迷宫的算法,为其寻找一条从入口点到出口点的通路。二维数组的第 0 行、第 m+1 行、第 0 列、第 m+1 列元素全置成“1” , 表示迷宫的边界;第 1 行第 1 列元素和第 m 行第 m 列元素置成“0” , 表示迷宫的入口和出口;其余元素值用随机函数产生。假设当前所在位置是(x,y) 。沿某个方向前进一步
9、,它可能到达的位置最多有 8 个。如果用二维数组 move 记录 8 个方向上行下标增量和列下标增量,则沿第 i 个方向前进一步,可能到达的新位置坐标可利用 move 数组确定:x=x+movei0y=y+movei1从迷宫的入口位置开始,沿图示方向顺序依次进行搜索。在搜索过程中,每前进一步,在所到位置处做标记“”(表示这个位置在通路上) ,并将该位置的坐标压入栈中。每次后退的时候,先将当前所在位置处的通路标记“” 改成死路标记“” (表示这个位置曾到达过但走不通,以后不要重复进入) ,然后将该位置的坐标从栈顶弹出。搜索到出口位置时,数组中那些值为“” 的元素形成一条通路。二、需求分析a.程序
10、的功能。(i) 实现一个以链表作存储结构的栈类型,以非递归算法求取所有通路和最短路径(ii)以一个递归算法,对任意输入的迷宫矩阵(1 代表不通,0 代表通路)求出所有通路 b.输入输出的要求。(i) 求得的通路以三元组(i,j,d)的形式输出,其中:(i,j)指示迷宫中的一个坐标,d 表示走到下一个坐标的方向。(ii)输出迷宫示意图c、程序算法分析6 7 8 5 1 4 3 2 x y o 81.迷宫的建立:迷宫中存在通路和障碍,为了方便迷宫的创建,可用 0 表示通路,用 1 表示障碍,这样迷宫就可以用 0、1 矩阵来描述,2.迷宫的存储:迷宫是一个矩形区域,可以使用二维数组表示迷宫,这样迷宫
11、的每一个位置都可以用其行列号来唯一指定,但是二维数组不能动态定义其大小,我们可以考虑先定义一个较大的二维数组 mazeM+2N+2,然后用它的前 m 行 n 列来存放元素,即可得到一个 mn 的二维数组,这样(0,0)表示迷宫入口位置,(m-1,n-1)表示迷宫出口位置。注:其中 M,N 分别表示迷宫最大行、列数,本程序 M、N 的缺省值为39、39,当然,用户也可根据需要,调整其大小。3.迷宫路径的搜索:首先从迷宫的入口开始,如果该位置就是迷宫出口,则已经找到了一条路径,搜索工作结束。否则搜索其上、下、左、右位置是否是障碍,若不是障碍,就移动到该位置,然后再从该位置开始搜索通往出口的路径;若
12、是障碍就选择另一个相邻的位置,并从它开始搜索路径。为防止搜索重复出现,则将已搜索过的位置标记为 2,同时保留搜索痕迹,在考虑进入下一个位置搜索之前,将当前位置保存在一个栈中,如果所有相邻的非障碍位置均被搜索过,且未找到通往出口的路径,则表明不存在从入口到出口的路径。这实现的是广度优先遍历的算法,如果找到路径,则为最短路径。三、概要设计i)设计中非递归程序的模块结构图图中方框表示函数,方框中指出函数名,箭头方向表示函数间的调用关系,虚线方框表示文件的组成main()mapath()9mgpath():求解迷宫问题,即输出从(1,1)到(M,N)的全部路径和最短路径(包含最短路径长度) 。当找到一
13、条路径时,不使用 return 语句退出,而是出栈一次,重新回溯走另一条路径,并用 minlen 记录最短路径长度,Path 数组记录最短路径。ii)程序的数据结构和数据库结构分析设迷宫为 m 行 n 列,利用 mazemn来表示一个迷宫,mazeij=0 或 1; 其中:0 表示通路,1 表示不通,当从某点向下试探时,中间点有 4 个方向可以试探, (见图)而四个角点有 2 个方向,其它边缘点有 3 个方向,为使问题简单化我们用 mazem+2n+2来表示迷宫,而迷宫的四周的值全部为 1。这样做使问题简单了,每个点的试探方向全部为 4,不用再判断当前点的试探方向有几个,同时与迷宫周围是墙壁这
14、一实际问题相一致。如图 3.4 表示的迷宫是一个 68 的迷宫。入口坐标为(1,1) ,出口坐标为(m,n) 。入口(1,1) 0 1 2 3 4 5 6 7 8 90 1 1 1 1 1 1 1 1 1 11 1 0 1 1 1 0 1 1 1 12 1 0 0 1 0 1 1 1 1 13 1 0 0 0 0 0 0 0 1 14 1 0 0 1 1 0 1 1 1 15 1 1 0 0 0 1 0 0 0 16 1 0 1 1 0 0 0 1 0 17 1 1 1 1 1 1 1 1 1 1出口 (6,8) 图 1 用 mazem+2n+2表示的迷宫迷宫的定义如下:#define m 6
15、 /* 迷宫的实际行 */#define n 8 /* 迷宫的实际列 */int maze m+2n+2 ;iii试探方向的设计示迷宫的情况下,每个点有 4 个方向去试探,如当前点的坐标( x , y),与其相邻的 4 个点的坐标都可根据与该点的相邻方位而得到,如图 2 所示。因为出口在(m,n) ,因此试探顺序规定为:从当前位置向前试探的方向为从正东沿顺时针方向进行。为了简化问题,方便的求出新点的坐标,将从正东开始沿顺时针进行的这 4 个方向(用0,1,2,3 表示东、南、西、北)的坐标增量放在一个结构数组 move 4 中,在move 数组中,每个元素有两个域组成, x:横坐标增量, y:
16、纵坐标增量。Move 数组如mn10图 3 所示。move 数组定义如下:typedef struct int x ; /行int y ; /列 item ; item move4 ;这样对 move 的设计会很方便地求出从某点 (x, y) 按某一方向 v (0v3) 到达的新点( i, j)的坐标: i =x + movev.x , j = y + movev.y 。iii、栈的设计当到达了某点而无路可走时需返回前一点,再从前一点开始向下一个方向继续试探。因此,压入栈中的不仅是顺序到达的各点的坐标,而且还要有从前一点到达本点的方向,即每走一步栈中记下的内容为(行,列,来的方向)。对于图 1
17、 所示迷宫,依次入栈为:栈中每一组数据是所到达 的每点的坐标及从该点沿哪个方向向下走的,对于图 3 迷宫,走x y0 0 11 1 02 0 -13 -1 0top 3,4,03,3,03,2,12,2,02,1,11,1,0(x,y)图 2 与点 (x,y) 相邻的 4 个点及坐标(x,y+1)(x,y-1)(x+1,y)(x-1,y)图 3 增量数组 move11的路线为:(1,1) 0(2,1)1(2,2)0(3,2)1(3,3)0(3,4)0(下脚标表示方向) ,当无路可走,则应回溯,对应的操作是出栈,沿下一个方向即方向继续试探。栈中元素是一个由行、列、方向组成的三元组,栈元素的设计如
18、下:typedef structint x , y , d ;/* 横纵坐标及方向*/datatype ;栈的定义为: SeqStack s ;iv、达某点,以避免发生死循环:一种方法是另外设置一个标志数组 markmn,它的所有元素都初始化为 0,一旦到达了某一点 ( i , j )之后,使 mark i j 置 1,下次再试探这个位置时就不能再走了。另一种方法是当到达某点( i , j)后使 maze i j 置 -1,以便区别未到达过的点,同样也能起到防止走重复点的目的,此处采用后一方法,算法结束前可恢复原迷宫。四、详细设计a.伪码设计(1) 栈初始化;(2) 将入口点坐标及到达该点的方
19、向(设为-1)入栈(3)while (栈不空) 栈顶元素(x , y , d)出栈 ;求出下一个要试探的方向 d+ ;while (还有剩余试探方向时) if (d 方向可走)则 (x , y , d)入栈 ;求新点坐标 (i, j ) ;将新点(i , j)切换为当前点(x , y) ;if ( (x ,)= =(,n) ) 结束 ;else 重置 d=0 ;else d+ ;算法如下:int path(int datetype temp ;int x, y, d, i, j ; temp.x=1 ; temp.y=1 ; temp.d=-1 ; Push_SeqStack (s,temp)
20、 ;阿while (! Empty_SeqStack (s ) ) 12Pop_SeqStack (s,temp) ;x=temp.x ; y=temp.y ; d=temp.d+1 ;while (d#include #define M 9 /行数#define N 8 /列数#define MaxSize 100 /栈最多元素个数int mgM+2N+2 = /迷宫测试数据矩阵,左上角(1,1)为入口,右下角(9,8)为出口1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,1,0,1,1,0,0,0,0,1,1,0,1,1,1,0,
21、1,1,1,0,0,1,0,1,1,0,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1;18struct int i;int j;int di; StackMaxSize,PathMaxSize; /定义栈和存放最短路径的数组int top=-1; /栈顶指针int count=1; /路径数计数int minlen=MaxSize; /最短路径长度void mgpath() /路径为:(1,1)-(M,N)in
22、t i,j,di,find,k;top+; /进栈Stacktop.i=1;Stacktop.j=1;Stacktop.di=-1;mg11=-1;/初始结点进栈while (top-1) /栈不空时循环i=Stacktop.i;j=Stacktop.j;di=Stacktop.di;if (i=M for (k=0;kint flag=0; /flag 用来标记是否路径全部走完int a1212=1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,1,0,1,1,0,0,0,0,1,1,0,1,1,1,0,1,1,1,0,0,1,0,1
23、,1,0,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,0,0,0,1,0,1,1,211,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1;int go(int x,int y)axy=2;if(x=9if(flag!=1 /这个 go()纠结了好久,多了个 return 怎么都不行了if(flag!=1if(flag!=1if(flag!=1if(flag!=1)axy=0;return flag;void main()int i,j,k,l,q;for(i=0;i11;i+) /输出迷宫for(j=0;j12;j+)if(aij=0)printf(“ “);if(aij=1)printf(“);22printf(“n“);printf(“n“);if(go(1,1)=0) /设置了起始点为 1,1printf(“没有路径!n“);elsefor(k=0;k12;k+) /输出迷宫for(l=0;l12;l+)if(akl=0)printf(“ “);if(akl=1)printf(“);if(akl=2)printf(“);printf(“n“);