数据结构课程设计迷宫问题
专业:计算机科学与技术
2008年10月20 日
数据结构课程设计
一、说明:
1、课程设计题目均选自《数据结构习题集》,请你根据所给页码及题目查阅相应内容,任选其一确定自己设计的题目;
2、题目一般分为基本要求和选做内容,选做内容将作为答优的基本要求;
3、课程设计的成绩分为两部分:系统演示+设计报告。
4、演示部分的检查在12教803室,在课程设计结束后一周。
5、时间:第8周周一无课时间,第8周周六、周日8:00-12:00,1:00-5:00,第9周周一无课时间。地点12教五楼机房。
二、题目:
P77: 0.3-海龟作图;
P80: 1.3-集合的并、交和差运算(或者1.4-长整数四则运算);
P105: 2.9-迷宫问题;
P152: 5.7-表达式类型的实现;
P153: 5.8-全国交通咨询模拟。
三、报告要求:完成以上实验内容并写出实验报告,报告应具有以下内容:
1、实验内容
2、概要设计
3、详细设计
4、测试数据及程序运行情况
5、实验过程中出现的问题及解决方法
6、实验体会
四、实验报告要求全部为打印稿,格式统一(见附件实验报告格式),在程序演示检查完成后一并教给老师。
五、课程设计期间有问题,请到12教803室找王永燕,周劲老师。
1、实验内容
【问题描述】
以一个m×n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。
【基本要求】
首先实现一个链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。求得的通路以三元组(i,j,d)的形式输出,其中:(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。
【实现提示】
计算机解迷宫通常用的是“穷举求解”方法,即从入口出发,顺着某一个方向进行探索,若能走通,则继续往前进;否则沿着原路退回,换一个方向继续探索,直至出口位置,求得一条通路。假如所有可能的通路都探索到则未能到达出口,则所设定的迷宫没有通解。
可以二维数组存储迷宫数据,通常设定入口点的下标为(1,1),出口点的下标为(n,n)。为处理方便起见,可以迷宫的四周加一圈障碍。对于迷宫任一位置,均可约定有东、南、西、北四个方向可通。
【选作内容】
(1)编写递归形式的算法,求得迷宫中所有可能的通路;
(2)以方阵形式输出迷宫及其通路。
2、概要设计
1)抽象数据类型定义描述
(对各类的成员及成员函数进行抽象描述,参见书或ppt及实验)① ADT T is
Data
当前位置的行坐标、当前位置的列坐标、走到下一位置的方向end ADT T
② ADT Linknode is
Data
数据域、指针域
Operation
Linknode //构造函数
用于构造结点
end ADT LinkNode
③ ADT Stack is
Data
栈顶指针
Operation
Stack //构造函数
输入:无
初始化栈:置空栈
~stack //析构函数
Push
输入:要进栈的项e
前置条件:无
动作:把e压入栈顶
输出:无
后置条件:栈顶增加了一个新结点,栈顶指针指向新结点
Pop
输入:无
前置条件:栈非空
动作:弹出栈顶元素
输出:返回栈顶元素的值
后置条件:删除栈顶元素
GetPop
动作:返回栈顶元素的值
Clear
动作:清空栈
empty
动作: 检查栈顶指示是否等于NULL
输出:栈空时返回1,否则返回0
end ADT Stack
2)功能模块设计(如主程序模块设计)
int** GetMaze(int &m,int &n) 返回存取迷宫的二维指针
bool Mazepath(int **maze,int m,int n)//寻找迷宫maze中从(0,0)到(m,n)的路径void PrintPath(Stack p) //输出路径
void Restore(int **maze,int m,int n) //恢复迷宫
主程序:用于调用其它函数
3)模块层次调用关系图
3、详细设计
1)实现概要设计中定义的所有的类的定义及类中成员函数,并对主要的模块写出伪码算法。
Class T 无成员函数
Class LinkNode 无成员函数
Class Stack Stack(){ top=NULL; }
~Stack(){}
void Push(T e)
{
LinkNode *P;
P=new LinkNode;
P->data=e;
P->next=top;
top=P;}
T Pop()
{
T Temp;
LinkNode *P;
P=top;
top=top->next;
Temp=P->data;
delete P;
return Temp;
}
T GetPop()
{ return top->data; }
void Clear(); bool empty();
2)输入的形式和输入值的范围
int** GetMaze(int &m,int &n)
{
int **maze; //定义二维指针存取迷宫
int i=0,j=0;
cout<<"请输入迷宫的长和宽:";
int a,b;cin>>a>>b; //输入迷宫的长和宽
cout<<"请输入迷宫内容:\n";
m=a;
n=b; //m,n分别代表迷宫的行数和列数
maze=new int *[m+2]; //申请长度等于行数加2的二级指针
for(i= 0;i { maze[i]=new int[n+2]; } for(i=1;i<=m;i++) //输入迷宫的内容,1代表可通,0代表不通for(j=1;j<=n;j++) cin>>maze[i][j]; for(i=0;i maze[i][0]=maze[i][n+1]=1; for(i=0;i maze[0][i]=maze[m+1][i]=1; return maze; //返回存贮迷宫的二维指针maze }; 以二维数组的形式,,一行一行的输入,定义二维指针来存储输入的二维数组,这样就能灵活的自定义数组范围。本题中二维数组为9行8列,因为入口为(1,1),在外面多加一圈障碍(2行2列)都赋值为1。 3)输出的形式描述 括号内的内容分别表示为(行坐标,列坐标,数字化方向,方向),由这些数据可以得到此迷宫的一条通路。 cout<<'('< switch(data.dir) //输出相应的方向{ case 1:cout<<"↓)\n";break; case 2:cout<<"→)\n";break; case 3:cout<<"↑)\n";break; case 4:cout<<"←)\n";break; case 0:cout<<")\n";break; 1:南2:东3:北4:西 4)功能描述 三个类: class T 定义描述迷宫中当前位置的数据类型 其公有变量为:x(行坐标)、y(列坐标)、dir(东南西北四个方向) Class LinkNode 链表结点定义 其公有变量为:T data, next域 Class Stack 链栈存储定义及功能实现 主要函数功能:创建栈、进栈、出栈、取栈顶值、清空栈等。int** GetMaze(int &m,int &n) 存取迷宫的二维指针函数,申请11行10列的指针空间,输入二位数组的内容,输入形式如上。完成后返回二维指 针,得到二维数组。 bool Mazepath(int **maze,int m,int n)寻找迷宫maze中从(0,0)到(m,n)的路径,到则返回true,否则返回false。定义两个栈p,q,分别存储比 较过程和存储路径,如果x行y列有元素值为0,则xy进栈p, 并设maze[x][y]=-1.当没有新元素进栈p时,说明当前元素周围 没有路径可以通过,让其出栈,直到退回到有路径可以再走的元 素那里,然后再判断此路是否能通下去。循环反复,判断能否走 到最后。 void Restore(int **maze,int m,int n) 用于恢复判断路径时被设为-1的值变为0。void PrintPath(Stack q) 把函数Mazepath生成的栈p出栈,后存入另一定义的栈t,逐一比较存入t的值和p栈顶的值,即可得出四个方向。然后 输出。 int main() 主函数 4、测试数据及程序运行情况 测试数据 迷宫的测试数据如下:左上角(1,1)为入口,右下角(4,4)为出口。 0 1 1 1 0 1 1 1 0 0 0 1 1 1 0 0 运行结果如下: 迷宫路径为 括号内的内容分别表示为(行坐标,列坐标,数字化方向,方向) (1,1,1,↓) (2,1,1,↓) (3,1,2,→) (3,2,2,→) (3,3,1,↓) (4,3,2,→) (4,4,0) 迷宫路径探索成功! 5、实验过程中出现的问题及解决方法 1)程序运行时就遇到了问题,程序没有错误但是无法运行,也不知哪里出了错误,最后只好重新运行VC程序; 2)在输出路径时显示的数字化方向和方向不一致,通过实践调试将方向设置为1↓;3↑; 2→;4←后显示正确。 6、实验体会 通过这次课程设计使我能够熟悉链栈的基本操作和应用,利用链表和栈解决简单的实际应用问题。锻炼了我解决问题的能力,充分体会到算法的时间复杂度和空间复杂度的特性。 这次实验使我知道先设计大致的程序模块在进行详细的程序设计使一种很好的程序设计 习惯,对我们更加全面详尽的了解实现程序的功能用重要的作用。 7.附录: 程序代码如下: #include #include using namespace std;//名称空间标识符std struct DataType //定义描述迷宫中当前位置的结构类型 { public: int x; //x代表当前位置的行坐标 int y; //y代表当前位置的列坐标 int pre; //0:无效,1:↓,2:→,3:↑,4:← }; struct Move {int x; int y; }; struct LinkNode //链表结点 { DataType data; LinkNode *next; }; struct Stack //定义栈 { private: LinkNode *top; //指向第一个结点的栈顶指针 public: Stack(); //构造函数,置空栈 ~Stack(); //析构函数 void Push(DataType data); //把元素data压入栈中 DataType Pop(); //使栈顶元素出栈 DataType GetPop(); //取出栈顶元素 void Clear(); //把栈清空 bool IsEmpty(); //判断栈是否为空,如果为空则返回1,否则返回0 }; Stack::Stack() //构造函数,置空栈 { top=NULL; } Stack::~Stack() //析构函数与构造函数相反,当对象脱离其作用域时,系统自动执行析构函数 {/* LinkNode *p=top; while(top!=NULL) { p=top; top=top->next; // delete p; }*/ } void Stack::Push(DataType e) //把元素x压入栈中 { LinkNode *TempNode; TempNode=new LinkNode; TempNode->data=e; TempNode->next=top; top=TempNode; } DataType Stack::Pop() //使栈顶元素出栈 { DataType Temp; LinkNode *TempNode; TempNode=top; top=top->next; Temp=TempNode->data; delete TempNode; return Temp; } DataType Stack::GetPop() //取出栈顶元素 { return top->data; } void Stack::Clear() //把栈清空 { top=NULL; } bool Stack::IsEmpty() //判断栈是否为空,如果为空则返回1,否则返回0 { if(top==NULL) return true; else return false; } int move[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; //定义当前位置移动的4个方向bool Mazepath(int **maze,int m,int n); //寻找迷宫maze中从(0,0)到(m,n)的路径 //到则返回true,否则返回false void PrintPath(Stack p); //输出迷宫的路径 void Restore(int **maze,int m,int n); //恢复迷宫 int** GetMaze(int &m,int &n); //获取迷宫 //返回存取迷宫的二维指针 int main() { int m=0,n=0; //定义迷宫的长和宽 int **maze; //定义二维指针存取迷宫 maze=GetMaze(m,n); //调用GetMaze(int &m,int &n)函数,得到迷宫 if(Mazepath(maze,m,n)) //调用Mazepath(int **maze,int m,int n)函数获取路径 cout<<"迷宫路径探索成功!\n"; else cout<<"路径不存在!\n"; return 0; } int** GetMaze(int &m,int &n)//返回存取迷宫的二维指针 { int **maze; //定义二维指针存取迷宫 int i=0,j=0; cout<<"请输入迷宫的长和宽:"; int a,b; cin>>a>>b; //输入迷宫的长和宽 cout<<"请输入迷宫内容:\n"; m=a; n=b; //m,n分别代表迷宫的行数和列数 maze=new int *[m+2]; //申请长度等于行数加2的二级指针 for(i=0;i { maze[i]=new int[n+2]; } for(i=1;i<=m;i++) //输入迷宫的内容,0代表可通,1代表不通for(j=1;j<=n;j++) cin>>maze[i][j]; cout<<"是否保存新迷宫?\n";//保存新迷宫 char choose; cin>>choose; if(choose=='Y'||choose=='y') { char ch; ofstream fop("Newtest.txt"); // //以二进制方式打开hfmTree.dat文件,并当重新运行时覆盖原文件 for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { ch='0'+maze[i][j]; fop< } fop< flush(cout);//将缓冲区的内容马上送进cout||把输出缓冲区刷新 } fop.close();////关闭文件 }//给迷宫的四周加一堵墙,即把迷宫四周定义为1 for(i=0;i maze[i][0]=maze[i][n+1]=1; for(i=0;i maze[0][i]=maze[m+1][i]=1; return maze; //返回存贮迷宫的二维指针maze }; bool Mazepath(int **maze,int m,int n)//寻找迷宫maze中从(0,0)到(m,n)的路径 //到则返回true,否则返回false { Stack q,p; //定义栈p、q,分别存探索迷宫的过程和存储路径 DataType Temp1,Temp2; int x,y,loop; Temp1.x=1; Temp1.y=1; q.Push(Temp1); //将入口位置入栈 p.Push(Temp1); maze[1][1]=-1; //标志入口位置已到达过 while(!q.IsEmpty()) //栈q非空,则反复探索 { Temp2=q.GetPop(); //获取栈顶元素 if(!(p.GetPop().x==q.GetPop().x&&p.GetPop().y==q.GetPop().y)) p.Push(Temp2); //如果成功入栈,则把上一个探索的位置存入栈p for(loop=0;loop<4;loop++) //探索当前位置的4个相邻位置 { x=Temp2.x+move[loop][0]; //计算出新位置x位置值 y=Temp2.y+move[loop][1]; //计算出新位置y位置值 if(maze[x][y]==0) //判断新位置是否可达 { Temp1.x=x; Temp1.y=y; maze[x][y]=-1; //标志新位置已到达过 q.Push(Temp1); //新位置入栈 } if((x==(m))&&(y==(n))) //成功到达出口 { Temp1.x=m; Temp1.y=n; Temp1.pre=0; p.Push(Temp1); //把最后一个位置入栈 PrintPath(p); //输出路径 Restore(maze,m,n); //恢复路径 return 1; //表示成功找到路径 } } if(p.GetPop().x==q.GetPop().x&&p.GetPop().y==q.GetPop().y) //如果没有成功入栈,则返回到上一个位置 { p.Pop(); q.Pop(); } } return 0; //表示查找失败 } void PrintPath(Stack p) //输出路径 { cout<<"迷宫的路径为\n"; cout<<"括号内的内容分别表示为(行坐标,列坐标,数字化方向,方向)\n"; Stack t; //定义一个栈,按从入口到出口存取路径 int a,b; DataType data; LinkNode *temp; temp=new LinkNode; //申请空间 temp->data=p.Pop(); //取栈p的顶点元素 t.Push(temp->data); //顶点元素入栈t delete temp; //释放空间 while(!p.IsEmpty()) //栈p非空,则反复转移 { temp=new LinkNode; temp->data=p.Pop(); //获取下一个位置,得到行走方向 a=t.GetPop().x-temp->data.x; //行坐标方向 b=t.GetPop().y-temp->data.y; //列坐标方向 if(a==1) temp->data.pre=1; //方向向南,用1表示 else if(b==1) temp->data.pre=2; //方向向东,用2表示 else if(a==-1) temp->data.pre=3; //方向向北,用3表示 else if(b==-1) temp->data.pre=4; //方向向西,用4表示 t.Push(temp->data); //把新位置入栈 delete temp; } //输出路径,包括行坐标,列坐标,下一个位置数字方向 while(!t.IsEmpty()) //栈非空,继续输出 { data=t.Pop(); cout<<'('< { case 1:cout<<"↓)\n";break; case 2:cout<<"→)\n";break; case 3:cout<<"↑)\n";break; case 4:cout<<"←)\n";break; case 0:cout<<")\n";break; } } } void Restore(int **maze,int m,int n) //恢复迷宫 { int i,j; for(i=0;i for(j=0;j { if(maze[i][j]==-1) //恢复探索过位置,即把-1恢复为0 maze[i][j]=0; } }