摘要本课题主要研究在C语言的基础上用栈和队列两种放过实现求解迷宫,迷宫用一个二维数组来表示。
求解迷宫通常用的是“穷举求解”的方法,即从入口出发,顺某一方向向前探索,若能走通,则继续往前进:否则沿原路退回,换一个方向再继续探索;直至所有可能的通路都探索为止。
关键词:迷宫;栈;队列;二维数组目录1问题描述 (1)2需求分析 (1)2.1课程设计目的 (1)2.2课程设计任务 (1)2.3设计环境 (1)3概要设计 (2)3.1数据结构设计 (2)3.2系统流程图 (4)4编码与实现 (6)4.1分析 (6)4.2具体代码实现 (9)5测试分析 (17)6课程设计总结 (18)参考文献 (19)致谢 (19)1问题描述设计一个简单迷宫程序,从入口出发,按某一方向向前探索,若能走通(未走过的),即某处可以到达,则到达新点,否则试探下一方向;若所有方向均没有通路,则沿原点返回前一点,换下一个方向在继续试探,直到所有可能的通路都探索到,或找到一条通路,或无路可走又返回到入口点。
并利用两种方法实现:一种用栈实现,另一种用队列实现。
2 需求分析2.1课程设计目的学生在教师指导下运用所学课程的知识来研究、解决一些具有一定综合性问题的专业课题。
通过课程设计(论文),提高学生综合运用所学知识来解决实际问题、使用文献资料、及进行科学实验或技术设计的初步能力,为毕业设计(论文)打基础。
2.2课程设计任务(1)定义一个二维数组存放迷宫数据;(2)画出查询模块的流程图;(3)编写代码;(4)程序分析与调试。
2.3设计环境(1)WINDOWS 2000/2003/XP/7/Vista系统(2)Visual C++或TC集成开发环境3 概要设计3.1数据结构设计(1)迷宫类型设迷宫为M行N列,利用maze[M][N]来表示一个迷宫,maze=0或1,其中0表示通路,1表示不通。
当从某点试探是,中间点有8个不同点可以试探,而四个角有3个方向,其他边缘点有5个方向,为使问题更容易分析我们用maze[M+2][N+2]来表示迷宫,而迷宫四周的值全部为1。
定义如下:#define M 6/*迷宫的实际行*/#define N 8 /*迷宫的实际列*/int maze[M+2][N+2];(2)队列的类型定义队列的有关数据结构、试探方向等和栈的求解方法处理基本相同。
不同的是:如何存储搜索路径。
在搜索过程中必须几下每一个可到达的坐标点,以便从这些点出发继续向四周搜索。
到达迷宫的出口点(m,n)后,为能够从出口沿搜索路径回溯直至入口,对于每一点,记下坐标点的同时,还要记下到达该点的前驱点,因此用一个结构体数组ele[MAX]作为队列的存储空间,因为每一点至少被访问一次,所以MAX 至多等于m*n。
该结构体有三个域:x、y和pre。
其中x、y分别为所到达点的坐标,pre为前驱点在elem中的下标。
除此之外,还需设定头、尾指针,分别指向队头,队尾。
类型定义如下:typedef struct //队的相关类型定义{ int x,y;int pre;}Elemtype;typedef struct //队列的类型定义{ Elemtype elem[MAXSIZE];int front,rear;int len;}SqQueue;(3)队列的相关模块定义函数DLmazepath( ),利用队列实现迷宫求。
定义函数DLmazepath( ),实现队列的迷宫路径输出。
定义函数I nitQueue(),实现队列的初始化。
定义函数QueueEmpty( ),判断队列是否为空,为空返回1,否则返回0.定义函数GetHead (SqQueue q,Elemtype *e),实现队头元素的读取。
定义函数EnQueue(SqQueue *q,Elemtype e),实现入队操作。
定义函数DeQueue(SqQueue *q,Elemtype *e),实现出队操作。
定义函数Sprint(int a[M+2][N+2]),实现,迷宫的输出。
定义栈相关的函数见同伴的报告。
3.2 系统流程图(1)主函数图3.1 主函数流程图(2)队列求解迷宫图3.2 队列求解迷宫流程图4 编码与实现4.1分析(1)主函数void main(){int a,i,j,maze2[M+2][N+2];/*构造一个迷宫*/int maze[M+2][N+2]={{1,1,1,1,1,1,1,1,1,1},{1,0,1,1,1,0,1,1,1,1},{1,1,0,1,0,1,1,1,1,1},{1,0,1,0,0,0,0,0,1,1},{1,0,1,1,1,0,1,1,1,1},{1,1,0,0,1,1,0,0,0,1},{1,0,1,1,0,0,1,1,0,1},{1,1,1,1,1,1,1,1,1,1}};item move[8]={{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};/*坐标增量数组move的初始化*/为使得程序更加人性化,更加友好,因此可将系统输出界面设置如下:printf(" |*****************迷宫求解系统*****************|\n");printf(" | 1 、栈方法求解迷宫的路径|\n");printf(" | 2 、队列求解的迷宫路径|\n");printf(" | 3、退出系统|\n");printf(" |*******************************************|\n");printf("\t\n\n请选择(0-3):"); scanf("%d",&a);while(a!=3){ switch(a){ Case 1:Sprint(maze);printf(“路径为:\n");Zmazepath(maze,move);break;Case2:Sprint(maze2);printf("路径:\n");DLmazepath(maze2,move);break;default : printf("\t\t选择错误!!\n");}printf("\t\n请选择(0-3).....\n"); scanf("%d",&a);}printf("\n\t\t非常感谢您的使用!\n");}(2)利用队列实现迷宫求解伪代码如下:int DLmazepath_(int maze[M+2][N+2],item move[8])/*采用队列的迷宫算法。
Maze[M+2][N+2]表示迷宫数组,move[8]表示坐标增量数组*/{队的初始化;将入口点坐标及到达该点的方向(设为-1)入队;while(队不为空){ for( 从1到8个方向)求新坐标点坐标,并将可到达点分别入队;if(点(x,y)为出口点)结束输出路径,迷宫有路;当前点搜索完8个方向后出队;}return o /*迷宫五路*/}void DLprintpath(SqQueue q)//输出迷宫路径,队列中保存的就是一条迷宫的通路{ int i; i=q.rear-1;do{ printf("(%d,%d)<--",(q.elem[i]).x,(q.elem[i]).y);i=(q.elem[i]).pre;}while(i!=-1)利用栈方法和队列方法用到的是同一个迷宫数组,在使用栈方法实现迷宫求解时,为避免死循环改变了原来的迷宫数组的个别路径值,因此使用队列求解时不能得到相应的路径解,为避免此,我们可在用栈方法求解迷宫路径之前将数组赋值给另一个数组,这样队列求解迷宫时可以再原来不改变的迷宫数组下进行。
具体实现代码如下:for(i=0;i<M+2;i++)for(j=0;j<N+2;j++){ maze2[i][j]=maze[i][j];}(3)队列的操作void InitQueue(SqQueue *q) /*队列的初始化*/{ 将队中元素赋值为0;}int QueueEmpty(SqQueue q) /*判队空*/{ if(队长度为0)返回1;else返回0;}void GetHead (SqQueue q,ElemType *e)/*读队头元素*/{ if(队的长度为0)输出提示队列为空;else 将队中值赋给e;}void EnQueue(SqQueue *q,ElemType e)/*入队*/{if(队列长度已满)输出提示;else{将e中元素赋给队列;队尾指针指向下一个元素;队长加1;}}void DeQueue(SqQueue *q,ElemType *e)/*出队*/{if(判队空)输出提示;else {将队中元素赋给e;队头指向下一个元素;队长减1;} }4.2 具体代码实现#include<stdio.h>#include<stdlib.h>#define M 6#define N 8#define MAXSIZE 100#define MAX M*Ntypedef struct //栈的相关类型定义{int x,y,d; //d 下一步方向}elemtype;typedef struct{elemtype data[MAXSIZE];int top;}Sqstack;typedef struct{int x,y;}item;typedef struct //队的相关类型定义{int x,y;int pre;}Elemtype;typedef struct //队列的类型定义{Elemtype elem[MAXSIZE];int front,rear;int len;}SqQueue;/* 栈函数*/void InitStack(Sqstack *s) //构造空栈{s->top=-1;}int Stackempty(Sqstack s) //判断栈是否为空{if(s.top==-1) return 1;else eturn 0;}void push(Sqstack *s,elemtype e) //入栈{if(s->top==MAXSIZE-1){ printf("Stack is full\n");return;}s->top++;s->data[s->top].x=e.x;s->data[s->top].y=e.y;s->data[s->top].d=e.d;}void pop (Sqstack *s,elemtype *e) // 出栈算法{if(s->top==-1){printf("Stack is empty\n");return;}e->x=s->data[s->top].x;e->y=s->data[s->top].y;e->d=s->data[s->top].d;s->top--;}/* 队函数*/void InitQueue(SqQueue *q) //队的初始化{q->front=q->rear=0;q->len=0;}int QueueEmpty(SqQueue q) //判断队空{if (q.len==0)return 1;else return 0;}void GetHead (SqQueue q,Elemtype *e)//读队头元素{if (q.len==0)printf("Queue is empty\n");else*e=q.elem[q.front];}void EnQueue(SqQueue *q,Elemtype e)//入队{if(q->len==MAXSIZE)printf("Queue is full\n");else{q->elem[q->rear].x=e.x;q->elem[q->rear].y=e.y;q->elem[q->rear].pre=e.pre;q->rear=q->rear+1;q->len++;}}void DeQueue(SqQueue *q,Elemtype *e) //出队{if(q->len==0)printf("Queue is empty\n");else{e->x=q->elem[q->rear].x;e->y=q->elem[q->rear].y;e->pre=q->elem[q->rear].pre;q->front=q->front+1;q->len--;}}void Sprint(int a[M+2][N+2]){int i,j;printf("迷宫为:\n");for(i=0;i<M+2;i++){for(j=0;j<N+2;j++)printf("%2d",a[i][j]);printf("\n");}}void Zprintpath(Sqstack s){ //输出迷宫路径,栈中保存的就是一条迷宫的通路elemtype temp;printf("(%d,%d)<--",M,N);while(!Stackempty(s)){pop(&s,&temp);printf("(%d,%d)<--",temp.x,temp.y);}printf("\n");}void Zmazepath(int maze[M+2][N+2],item move[8]) { //栈的迷宫求解输出Sqstack s;elemtype temp;int x,y,d,i,j;InitStack(&s);//栈的初始化temp.x=1;temp.y=1;temp.d=-1;push(&s,temp);while(!Stackempty (s)){pop(&s,&temp);x=temp.x;y=temp.y;d=temp.d+1;while(d<8){i=x+move[d].x;j=y+move[d].y;if(maze[i][j]==0){temp.x=x;temp.y=y;temp.d=d;push(&s,temp);x=i;y=j;maze[x][y]=-1;if(x==M&&y==N){Zprintpath(s);return;}else d=0;}//ifelse d++;}//while} //whilereturn;printf("迷宫无路\n");return;}void DLprintpath(SqQueue q){//输出迷宫路径,队列中保存的就是一条迷宫的通路int i;i=q.rear-1;do{printf("(%d,%d)<--",(q.elem[i]).x,(q.elem[i]).y);i=(q.elem[i]).pre;} while(i!=-1);printf("\n");}void DLmazepath(int maze1[M+2][N+2],item move[8]) { //队列的迷宫求解SqQueue q;Elemtype head,e;int x,y,v,i,j;InitQueue(&q); //队列的初始化e.x=1;e.y=1;e.pre=-1;EnQueue (&q,e);maze1[1][1]=-1;while(!QueueEmpty (q)){ GetHead(q,&head);x=head.x;y=head.y;for(v=0;v<8;v++){ i=x+move[v].x;j=y+move[v].y;if(maze1[i][j]==0){ e.x=i;e.y=j;e.pre=q.front;EnQueue(&q,e);maze1[x][y]=-1;} //ifif(i==M&&j==N){ DLprintpath(q);return ;}} //forDeQueue(&q,&head);}//whileprintf("迷宫无路!\n");return;}void main(){ int a,i,j,maze2[M+2][N+2];int maze[M+2][N+2]={{1,1,1,1,1,1,1,1,1,1},{1,0,1,1,1,0,1,1,1,1},{1,1,0,1,0,1,1,1,1,1},{1,0,1,0,0,0,0,0,1,1},{1,0,1,1,1,0,1,1,1,1},{1,1,0,0,1,1,0,0,0,1},{1,0,1,1,0,0,1,1,0,1},{1,1,1,1,1,1,1,1,1,1}};/*构造一个迷宫*/item move[8]={{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};/*坐标增量数组move的初始化*/for(i=0;i<M+2;i++)for(j=0;j<N+2;j++){ maze2[i][j]=maze[i][j];}printf(" |******************迷宫求解系统******************|\n"); printf(" | |\n"); printf(" | 1 、栈求解迷宫的路径|\n"); printf(" | |\n"); printf(" | 2 、队列求解的迷宫路径|\n"); printf(" | |\n"); printf(" | 3 、退出系统|\n"); printf(" | |\n"); printf(" |**********************************************|\n"); printf(" \t\n\n请选择(0-3):"); scanf("%d",&a);while(a!=3){switch(a){case 1:Sprint(maze); printf("求解路径为:\n");Zmazepath(maze,move);break;case 2:printf("求解路径为:\n");DLmazepath(maze2,move);break;default : printf("\t\t选择错误!!\n");}printf("\t\n请选择(0-3):"); scanf("%d",&a);}printf("\n\t\t结束退出程序!\n");}5 测试分析测试数据及结果如下:(1)系统友好界面输出图5.1 进入系统界面运行结果(2)选择1,运行结果输出如下:图5.2 迷宫以及使用栈求解迷宫路径的输出(3)选择2、3 运行结果如下:图5.3 迷宫以及使用队列求解迷宫路径的输出(4)选择3运行结果如下:图5.3 退出程序根据结果分析:利用栈求得的路径不一定是最短路径,而用队列求得的路径是最短路径。