迷宫问题课程设计.docx
《迷宫问题课程设计.docx》由会员分享,可在线阅读,更多相关《迷宫问题课程设计.docx(31页珍藏版)》请在冰豆网上搜索。
迷宫问题课程设计
题目:
迷宫问题
初始条件:
以一个m×n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。
设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。
首先实现一个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。
求得的通路以三元组(i,j,d)的形式输出,其中:
(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。
如:
对于下列数据的迷宫,输出的一条通路为:
(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2),…。
测试用例见题集p105。
要求完成的主要任务:
(包括课程设计工作量及其技术要求,以及说明书撰写等具体要求)
课程设计报告按学校规定格式用A4纸打印(书写),并应包含如下内容:
1、问题描述
简述题目要解决的问题是什么。
2、设计
存储结构设计、主要算法设计(用类C语言或用框图描述)、测试用例设计;
3、调试报告
调试过程中遇到的问题是如何解决的;对设计和编码的讨论和分析。
4、经验和体会(包括对算法改进的设想)
5、附源程序清单和运行结果。
源程序要加注释。
如果题目规定了测试数据,则运行结果要包含这些测试数据和运行输出,
6、设计报告、程序不得相互抄袭和拷贝;若有雷同,则所有雷同者成绩均为0分。
时间安排:
1、第19周完成。
2、6月22日14:
00到计算中心检查程序、交课程设计报告、源程序(CD盘)。
指导教师签名:
年月日
系主任(或责任教师)签名:
年月日
迷宫问题
1、问题描述与需求分析
1.1问题描述
以一个m×n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。
设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。
首先实现一个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。
求得的通路以三元组(i,j,d)的形式输出,其中:
(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。
如:
对于下列数据的迷宫,输出的一条通路为:
(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2),…
1.2需求分析
(1)以二维数组Maze[m+2][n+2]表示迷宫,其中:
Maze[0][j]和Maze[m+1][j](0<=j<=n+1)及Maze[i][0]和Maze[i][n+1](0<=i<=n+1)为添加的一圈障碍。
数组中一元素值为0表示通路,1表示障碍。
(2)其中迷宫的入口位置和出口位置可由用户随时设定。
(3)迷宫内墙的编写,用1表示内墙,0表示通路。
(4)以链表作存储结构的栈类型,实现求解迷宫的非递归程序。
(5)本程序只求出一条成功的通路,然而,只需要对迷宫求解的函数作小量修改,便可求得全部路径。
2、设计
2.1设计原理
主要采取三大模块:
主程序模块、栈模块和迷宫模块
栈模块:
实现迷宫数据的抽象化和对迷宫数据的处理;
迷宫模块:
实现迷宫数据抽象类型;
主程序模块:
初始化迷宫模块。
栈模块——实现栈抽象数据类型
迷宫模块——实现迷宫抽象数据类型
主程序模块:
Voidmain()
{
初始化;
do{
接受命令;
处理命令;
}while(命令!
=“退出”);
}
各模块之间的调用关系如下:
2.2储存结构设计
2.2.1设定栈的抽象数据类型定义:
ADTStack{
数据对象:
D={ai|ai∈CharSet,i=1,2,…,n,n≥0}
数据关系:
R1={|ai-1,ai∈D,i=2,…,n}
基本操作:
InitStack(&S)
操作结果:
构造一个空栈S。
DestroyStack(&S)
初始条件:
栈S已存在。
操作结果:
销毁栈S。
ClearStack(&S)
初始条件:
栈S已存在。
操作结果:
将S清为空栈。
StackLength(S)
初始条件:
栈S已存在。
操作结果:
返回栈S的长度。
StackEmpty(S)
初始条件:
栈S已存在。
操作结果:
若S为空栈,则返回TRUE,否则返回FALSE。
GetTop(S,&e)
初始条件:
栈S已存在。
操作结果:
若栈S不空,则以e返回栈顶元素。
Push(&S,e)
初始条件:
栈S已存在。
操作结果:
在栈S的栈顶插入信的栈顶元素e。
Pop(&S,&e)
初始条件:
栈S已存在。
操作结果:
删除S的栈顶元素,并以e返回其值。
StackTraverse(S,visit())
初始条件:
栈S已存在。
操作结果:
从栈底到栈顶一次对S中的每个元素调用函数visit()。
}ADTStack
2.2.2设定迷宫的抽象数据类型为:
ADTmaze{
数据对象:
D={ai,j|ai,j∈{0,1},0≤i≤m+1,0≤j≤n+1,m,n≤10}
数据关系:
R={ROW,COL}
ROW={|ai-1,j,ai-1∈D,i=1,…,m+1,j=0,…,n+1}
COL={|ai,j-1,ai-1∈D,i=1,…,m+1,j=0,…,n+1}
InitMaze(&M,a,row,col)
初始条件:
二维数组a[row+2][col+2]已经存在,其中自第一行至第row+1行、每行中自第1列至第col+1列的元素已经有值,并且以值0表示通路,以值1表示障碍。
操作结果:
构成迷宫的字符型数组,并在迷宫四周加上一圈障碍。
MazePath(&M)
初始条件:
迷宫M已被赋值。
操作结果:
若迷宫M中存在一条通路,则按照所走的步骤,从小到大依次排列。
PrintMaze(M)
初始条件:
迷宫M已存在。
操作结果:
已字符形式输出迷宫。
}ADTmaze;
设定当前为出始值的入口:
do{
若当前位置可通,
则{将当前位置插入栈顶;
若该位置是出口位置,则结束;
否则切换当前位置的东邻方块为新的当前位置;
}
否则{
若栈不空且栈顶位置尚有其他方向未被探索,
则设定新的当前位置为沿顺时针方向旋转找到的栈顶位置的下一邻块;
若栈不为空且栈顶位置的四周均不可通,
则{删除栈顶位置;
若栈不为空,则重新测试新的栈顶位置,
直至找到一个可通的相邻块或出栈至栈空;
}
}
2.3详细设计
2.3.1模块:
迷宫模块
以二维数组Maze[m+2][n+2]表示迷宫,其中:
Maze[0][j]和Maze[m+1][j](0<=j<=n+1)及Maze[i][0]和Maze[i][n+1](0<=i<=n+1)为添加的一圈障碍。
数组中一元素值为0表示通路,1表示障碍,限定迷宫的大小m,n<=10。
其中迷宫的入口位置和出口位置可由用户随时设定。
(1)坐标位置类型:
typedefstruct{
intr,c;//迷宫中r行c列的位置
}PosType;//坐标位置类型
(2)迷宫类型:
typedefstruct{
intm,n;
chararr[RANGE][RANGE];//各位置取值'','#','@'或'*'
}MazeType;//迷宫类型
栈模块
(1)栈类型SElemType:
typedefstruct{
intstep;//当前位置在路径上的“序号”
PosTypeseat;//当前的坐标位置
directiveTypedi;//往下一坐标位置的方向
}ElemType;//栈的元素类型
(2)构造一个栈SElmType:
structSqStack //SqStack
{
SElemType*base;/*在栈构造之前和销毁之后,base的值为NULL*/
SElemType*top;/*栈顶指针*/
intstacksize;/*当前已分配的存储空间,以元素为单位*/
};/*顺序栈*/
其中基本操作如下:
栈的初始化:
boolInitStack(SqStack*S)
{/*构造一个空栈S*/
(*S).base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!
(*S).base)
exit
(1);/*存储分配失败*/
(*S).top=(*S).base;
(*S).stacksize=STACK_INIT_SIZE;
returntrue;
}
元素进栈:
boolPush(SqStack*S,SElemTypee)
{/*插入元素e为新的栈顶元素*/
if((*S).top-(*S).base>=(*S).stacksize)/*栈满,追加存储空间*/
{
(*S).base=(SElemType*)realloc((*S).base,((*S).stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!
(*S).base)
exit
(1);/*存储分配失败*/
(*S).top=(*S).base+(*S).stacksize;
(*S).stacksize+=STACKINCREMENT;
}
*((*S).top)++=e;
returntrue;
}
判断栈是否为空:
boolStackEmpty(SqStackS)
{/*若栈S为空栈,则返回true,否则返回false*/
if(S.top==S.base)
returntrue;
else
returnfalse;
}
删除栈顶元素使之为空:
boolPop(SqStack*S,SElemType*e)
{/*若栈不空,则删除S的栈顶元素,用e返回其值,并返回true;否则返回false*/
if((*S).top==(*S).base)
returnfalse;
*e=*--(*S).top;
returntrue;
}
boolMazePath(PosTypestart,PosTypeend)/*算法3.3*/
{/*若迷宫maze中存在从入口start到出口end的通道,则求得一条*/
/*存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE*/
SqStackS;
PosTypecurpos;
SElemTypee;
InitStack(&S);
curpos=start;
do
{
if(Pass(curpos))
{/*当前位置可以通过,即是未曾走到过的通道块*/
FootPrint(curpos);/*留下足迹*/
e.ord=curstep;
a[curstep]=e.seat.x=curpos.x;
b[curstep]=e.seat.y=curpos.y;
c[curstep]=e.di=0;
Push(&S,e);/*入栈当前位置及状态*/
curstep++;/*足迹加1*/
if(curpos.x==end.x&&curpos.y==end.y)/*到达终点(出口)*/
returntrue;
curpos=NextPos(curpos,e.di);
}
else
{/*当前位置不能通过*/
if(!
StackEmpty(S))
{
Pop(&S,&e);/*退栈到前一位置*/
curpos=e.seat;
--curstep;
while((e.di==3)&&(!
StackEmpty(S)))//前一位置处于最后一个方向(北)
{
MarkPrint(e.seat);/*留下不能通过的标记(-1)*/
Pop(&S,&e);/*退回一步*/
curstep--;
}
if(e.di<3)/*没到最后一个方向(北)*/
{
e.di++;/*换下一个方向探索*/
Push(&S,e);
a[curstep]=e.seat.x=curpos.x;
b[curstep]=e.seat.y=curpos.y;
c[curstep]=e.di;
curstep++;
curpos=NextPos(e.seat,e.di);//设定当前位置是该新方向上的相邻块
}
}
}
}while(!
StackEmpty(S));
returnfalse;
}
(3)结点类型,指针类型:
typedefstructNodeType{
ElemTypedata;
NodeType*next;
}NodeType,*LinkType;
(4)以链表作存储结构的栈类型:
typedefstruct{
LinkTypetop;
intsize;
}Stack;
2.3.2主程序模块:
Void main()
{
Do{
接受命令;
处理命令;
}while(命令!
=“退出”)
}
程序如下:
//---------------------------------------主函数main()--------------------------------------------
voidmain(void)
{intcmd;
Initialization();//初始化
do{
ReadCommand(cmd);//读入一个操作命令符
Interpret(cmd);//解释一个操作命令符
}while
(1);
}//main()
2.3.3函数调用关系的层次结构框图:
主程序
Same
NextPos
MarkPrint
Pass
FootPrint
Push
InitStack
InitMaze
Initialization
2.4测试用例设计
0
1
1
1
0
0
1
1
0
0
0
1
0
0
0
0
此迷宫有4*4的迷宫,从这个迷宫中可以再一定程度上看出其再里面的路径,看到解决的途径。
3、调试报告
3.1遇到的问题及解决办法
(1)这次的课程设计程序大多来自于教科书和练习册,相对比较容易。
但是还在做课程设计的时候遇到了一些阻碍,一开始的时候的高兴没了,我认真的分析问题,但是还是有些失误。
我出翻资料照材料,解决问题。
(2)在写函数的时候发现有很多地方出现了小错误导致出现运行出现错误,如:
在初始化的时候因为变量的重名导致运行不过,经过了无数次的检查,终于找到了错误,经过深刻的自我反省,自己的粗心导致的错误实在是不应该出现。
(2)在编写MazePath函数时,当遇到墙(即遇到下一位置为1)时,直接从现在墙位置进行往南跳转。
以至有许多应该走的通路位置没有走,而且使总共走的步数变短。
在测试前期怎么也想不明白,出栈操作也有,curstep退位也有,但就是不进行退到上一位置的操作。
最后发现,少了一步把出栈的数进行赋值的操作。
只要进行curpos=e.seat的操作,上面的问题就迎刃而解了。
(3)在进行对迷宫的输出时,变成按行输出,得不到预期的迷宫结果,更不用说验证其正确性。
这就是粗心造成的,本来为简洁,就不用在下行再写cout了,直接就在cout<3.2对设计和编码的讨论与分析
(1)刚着手这个迷宫问题,首先要解决的就有两大问题,如何储存这样的迷宫,如何实现求解迷宫?
由于我们用的计算机的高计算特点,我们可以用穷举法来寻找迷宫的解。
(2)又有问题了,如何穷举?
该怎么穷举就不会出错?
根据习题册上的实例,可以分为四个方向挨个探索。
至于,如何储存并实现这个迷宫,就得依赖于栈和二维数组。
(3)在具体编码时,也会遇到很多问题,譬如根据书上的一些代码的运用会出现错误,需要自己动手,动脑亲自编写。
这样有助于对数据结构的理解,同时也达到了这门课程设计的实验目的。
(4)在运行代码的时候发现其实许多地方在以前的学习中掌握的不够好,有的地方还是有点模糊,不是很清楚。
经过不断的看书学习,把掌握的不够好的地方再次温习了一下。
4、经验和体会
通过这次的数据结构课程设计,让我学会了很多,譬如迷宫的实现,面对问题时应该怎样解决。
同时,也让我对栈这一章节有更深的体会,以及用不同的方法解决同意问题。
相比较得出较好的解决方案。
数据结构课程设计的主要目的是介绍一些常用的数据结构,阐明数据结构内在的逻辑关系,讨论它们在计算机中的存储表示,并结合各种数据结构,讨论对他们实行的各种运算的实现算法。
拿实现迷宫解的通路来说,我发现其实也不是很容易,一步步的走,电脑室很笨的只知道一步步的走,走不动了就回头,看到这一点,我就使用栈来解决问题,进栈,出栈,一步步解决通路的问题,在我的课程设计当中我用了几乎所有学过的数据结构,不单单只有栈这一种。
通过本次数据结构课程设计,让我学到了很多有用的知识,在实际操作中也犯了很多错误,这些错误同时也让我意外的收获了很多。
对我所学的数据结构知识理论也得到巩固。
通过实际的设计和分析,让我学会了编程的基本步骤和方法,同时也开发了自己的逻辑思维能力,培养了份额系问题、解决问题的能力。
在不断的遇到问题,不断的解决问题的过程中,培养的专业的思维是最重要的,也是这次课程设计所要达到的目的,我很庆幸我做到了。
5、源程序和运行结果
5.1源程序
//---------------------------------------------迷宫求解-------------------------------------------
#include
#include
#include
#defineTRUE1
#defineFALSE0
#defineOK1
#defineERROR0
#defineOVERFLOW-2
#defineRANGE100
typedefintStatus;
typedefintdirectiveType;
typedefstruct{
intr,c;//迷宫中r行c列的位置
}PosType;//坐标位置类型
typedefstruct{
intm,n;
chararr[RANGE][RANGE];//各位置取值'','#','@'或'*'
}MazeType;//迷宫类型
typedefstruct{
intstep;//当前位置在路径上的“序号”
PosTypeseat;//当前的坐标位置
directiveTypedi;//往下一坐标位置的方向
}ElemType;//栈的元素类型
typedefstructNodeType{
ElemTypedata;
NodeType*next;
}NodeType,*LinkType;//结点类型,指针类型
typedefstruct{
LinkTypetop;
intsize;
}Stack;//以链表作存储结构的栈类型
voidInitialization(void);
voidReadCommand(int&);
voidInterpret(int);
voidInitStack(Stack&);
StatusStackEmpty(Stack);
StatusPush(Stack&,ElemType);
StatusPop(Stack&,ElemType&);
StatusSame(PosType,PosType);
voidInitMaze(MazeType&,int*,int,int);
StatusPass(MazeType,PosType);
voidFootPrint(MazeType&,PosType);
voidMarkPrint(MazeType&,PosType);
PosTypeNextPos(PosType,directiveType);
StatusMazePath(MazeType&,PosType,PosType);
voidPrintMaze(MazeType);
intrnum,cnum;
MazeTypema;
//---------------------------------------主函数main()--------------------------------------------
voidmain(void)
{intcmd;
Initialization();//初始化
do{
ReadCommand(cmd);//读入一个操作命令符
Interpret(cmd);//解释一个操作命令符
}while
(1);
}//main()
//-------------------------------初始化函数Initialization()-------------------------------------
voidInitialization(){
cout<<"******************************************************************"<cout<<"*这是一个迷宫求解的程序*"<cout<<"*设计者:
徐旭东*"<co