数据结构课程设计-迷宫求解问题3080_第1页
数据结构课程设计-迷宫求解问题3080_第2页
数据结构课程设计-迷宫求解问题3080_第3页
数据结构课程设计-迷宫求解问题3080_第4页
数据结构课程设计-迷宫求解问题3080_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

.《数据结构课程设计:迷宫》实验报告任务分配:程序员:主要任务:负责整体的算法设计以及程序的主要源代码的编写。测试员:主要任务:负责在程序员每完成一个阶段对程序进行挑错,测试主程序并对实验结果进行整理分析,最后完成实验报告的第三、四部分即测试结果与分析探讨的内容。文档员:主要任务:负责对程序及界面的美观提出改善意见,查找程序的小漏洞,负责撰写实验报告的第一、二部分即实验内容简介与算法描述的内容。同时完成整个文档的整合,使整篇报告排版、文字风格统一。一、简介图的遍历就是从指定的某个顶点(称其为初始点)出发,按照一定的搜索方法对图中的所有顶点各做一次访问过程。根据搜索方法不同,遍历一般分为深度优先搜索遍历和广度优先搜索遍历。本实验中用到的是广度优先搜索遍历。即首先问访初始点vi,并将其标记为已问访过,接着问访vi的所有未被问访过的邻接点,顺序任意,并均标记为已问访过,以此类推,直到图中所有和初始点vi有路径相通的顶点都被问访过为止。鉴于广度优先搜索是将所有路径同时按照顺序遍历,直到遍历出迷宫出口,生成的路径为最短路径。因此我们采用了广度优先搜索。无论是深度优先搜索还是广度优先搜索,其本质都是将图的二维顶点结构线性化的过程,并将当前顶点相邻的未被问访的顶点作为下一个顶点。广度优先搜索采用队列作为数据可编辑

.结构。本实验的目的是设计一个程序,实现手动或者自动生成一个n×m矩阵的迷宫,寻找一条从入口点到出口点的通路。具体实验内容如下:选择手动或者自动生成一个n×m的迷宫,将迷宫的左上角作入口,右下角作出口,设“0”为通路,“1”为墙,即无法穿越。假设一只老鼠从起点出发,目的为右下角终点,可向“上、下、左、右、左上、左下、右上、右下”8个方向行走。如果迷宫可以走通,则用“■”代表“1”,用“□”代表“0”,用“☆”代表行走迷宫的路径。输出迷宫原型图、迷宫路线图以及迷宫行走路径。如果迷宫为死迷宫,则只输出迷宫原型图。二、算法说明根据实验内容,本实验主要设计实现手动输入迷宫,判断迷宫能否走通;自动生成迷宫,判断迷宫能否走通。迷宫算法的整体思想如下:.1、迷宫的创建迷宫中存在通路和障碍,为了方便迷宫的创建,可用0表示通路,用1表示障碍,这样迷宫就可以用0、1矩阵来描述。设置迷宫的长为n、宽为m,范围为49×49,用intmaze[N+2][M+2]来表示,这样相当于在迷宫外层包了一层1,即防止搜索路径时跳出迷宫。(1)手动生成迷宫voidhand_maze(intm,intn)//手动生成迷宫{inti,j;for(i=0;i<m;i++)for(j=0;j<n;j++){cin>>maze[i][j];可编辑.}}自动生成迷宫(2)voidautomatic_maze(intm,intn)//自动生成迷宫{inti,j;for(i=0;i<m;i++)for(j=0;j<n;j++)maze[i][j]=rand()%2;maze[0][0]=0;来迷宫//随机生成0、1//将开始和结束位置强制为0,保证有可能出maze[m-1][n-1]=0;}2、迷宫路径的搜索在生成的0、1矩阵迷宫中,首先从迷宫的入口开始,如果该位置就是迷宫出口,则已经找到了一条路径,搜索工作结束。否则搜索其北(-1,0),东北(-1,-1),东(0,1),东南(1,1),南(1,0),西南(1,-1),西(0,-1),西北(-1,-1)8个方向位,是否是障碍,若不是障碍,就移动到该位置,然后再从该位置开始搜索通往出口的路径;若是障碍就选择另一个相邻的位置,并从它开始搜索路径。为防止搜索重复出现,则将已搜索过的位置标记为2,同时保留搜索痕迹,在考虑进入下一个位置搜索之前,将当前位置保存在一个队列中,如果所有相邻的非障碍位置均被搜索过,且未找到通往出口的路径,则表明不存在从入口到出口的路径。这实现的是广度优先遍历的算法,如果找到路径,则为最路短径。逆序输出路径,将已输出的路径标记为3。实验数据如下:列0行01230101可编辑.101100111000023123456789(0,0)(1,1)(1,0)(0,2)(2,1)(1,3)(3,2)(2,3)(3,3)-111223456算法如下:intpath(intmaze[51][51],intm,intn)//路径求解{X=1;//初始值定为1structpointp={0,0,-1};//定义入口节点if(maze[p.row][p.col]==1)//入口为1时,迷宫不可解{cout<<"此迷宫无解\n\n";X=0;return0;}maze[p.row][p.col]=2;enqueue(p);while(!is_empty()){//标记为已访问//将p入队列p=dequeue();if((p.row==m-1)&&(p.col==n-1))break;//当行和列为出口时跳出//定义8个走位方向if((((p.row-1)>=0)&&((p.row-1)<m)&&((p.col+0)<n))&&(maze[p.row-1][p.col+0]==0))visit(p.row-1,p.col+0,maze);//北if((((p.row-1)>=0)&&((p.row-1)<m)&&((p.col+1)<n))&&(maze[p.row-1][p.col+1]==0))visit(p.row-1,p.col+1,maze);//东北if((((p.row+0)<m)&&((p.col+1)<n))&&(maze[p.row+0][p.col+1]==0))visit(p.row+0,p.col+1,maze);//东if((((p.row+1)<m)&&((p.col+1)<n))&&(maze[p.row+1][p.col+1]==0))visit(p.row+1,p.col+1,maze);//东南可编辑.if((((p.row+1)<m)&&((p.col+0)<n))&&(maze[p.row+1][p.col+0]==0))visit(p.row+1,p.col+0,maze);//南if((((p.row+1)<m)&&((p.col-1)<n)&&((p.col-1)>=0))&&(maze[p.row+1][p.col-1]==0))visit(p.row+1,p.col-1,maze);//西南if((((p.row+0)<m)&&((p.col-1)<n)&&((p.col-1)>=0))&&(maze[p.row+0][p.col-1]==0))visit(p.row+0,p.col-1,maze);//西if((((p.row-1)>=0)&&((p.row-1)<m)&&((p.col-1)<n)&&((p.col-1)>=0))&&(maze[p.row-1][p.col-1]==0))visit(p.row-1,p.col-1,maze);//西北}if(p.row==m-1&&p.col==n-1)//如果当前矩阵点是出口点,输出路径{cout<<"迷宫路径为:\n";cout<<"出口"<<endl;cout<<""<<"↑"<<endl;printf("(%d,%d)\n",p.row+1,p.col+1);cout<<""<<"↑"<<endl;maze[p.row][p.col]=3;//逆序将路径标记为3while(p.predecessor!=-1){p=queue[p.predecessor];printf("(%d,%d)\n",p.row+1,p.col+1);cout<<""<<"↑"<<endl;maze[p.row][p.col]=3;}cout<<"入口"<<endl;}else{cout<<"此迷宫无解!\n\n";X=0;}return0;}3、输出迷宫图(1)、生成迷宫图,将迷宫外壳输出为▲,将迷宫中的0输出为□,将1输出为■for(k=0;k<n;k++)可编辑

.{}cout<<"▲";//这两个黑三角用来生成顶部外壳for(i=0;i<m;i++){cout<<"\n";cout<<"▲";for(j=0;j<n;j++){//生成左外壳if(maze[i][j]==0)cout<<"□";if(maze[i][j]==1)cout<<"■";}cout<<"▲";//生成右外壳}cout<<endl;for(k=0;k<n;k++){cout<<"▲";}cout<<"▲\n";//生成底部外壳(2)、生成迷宫路径图,for(i=0;i<m;i++){cout<<"\n";for(j=0;j<n;j++){if(maze[i][j]==0||maze[i][j]==2)cout<<"□";//2是队列中访问过的点if(maze[i][j]==1)cout<<"■";if(maze[i][j]==3)cout<<"☆";//3是标记的可以走通的路径}}(3)总界面的实现考虑到添加画图部分,对我们的原程序改动较大,因此我们没有采用画图画线输出迷宫路径,同时,我们也扩充了迷宫的功能,可以定义任意宽度和长度的迷宫并实现手动、自动生成迷宫等功能。可编辑

.输出界面有三个选项,1为手动生成迷宫,2为自动生成迷宫,3为推出程序。当程序运行时,设cycle为0,当选择3退出程序时,cycle为-1。voidmain(){inti,m,n,cycle=0;while(cycle!=(-1)){switch(i){case1:cout<<"\n请输入行数:";//手动输出迷宫cin>>m;cout<<"\n";cout<<"请输入列数:";cin>>n;while((m<0||m>49)||(n<0||n>49)){cout<<"\n抱歉,你输入的行列数超出预设范围(0-49,0-49),请重新输入\n\n";cout<<"\n请输入行数:";cin>>m;cout<<"\n";cout<<"请输入列数:";cin>>n;}shoudong_maze(m,n);data(m,n);print_maze(m,n);mgpath(maze,m,n);if(X!=0)result_maze(m,n);cout<<"\n\nPressEnterContiue!\n";getchar();while(getchar()!='\n');break;case2://自动输出迷宫cout<<"\n请输入行数:";cin>>m;cout<<"\n";cout<<"请输入列数:";cin>>n;while((m<0||m>49)||(n<0||n>49)){cout<<"\n抱歉,你输入的行列数超出预设范围(0-49,0-49),请重新输入\n\n";可编辑

.cout<<"\n请输入行数:";cin>>m;cout<<"\n";cout<<"请输入列数:";cin>>n;}zidong_maze(m,n);data(m,n);print_maze(m,n);mgpath(maze,m,n);if(X!=0)result_maze(m,n);cout<<"\n\nPressEnterContiue!\n";getchar();while(getchar()!='\n');break;case3://程序退出cycle=(-1);break;default://其他情况时,输出有误cout<<"\n";cout<<"你的输入有误!\n";cout<<"\nPressEnterContiue!\n";getchar();while(getchar()!='\n');break;}}}三.测试结果本设计采用广度优先的算法,实现迷宫的路径求解,在原要求的基础上,加以扩展,实现要求范围内的N×N,N×M的手动、自动生成迷宫矩阵的求解。(一)基本功能测试1、手动迷宫(1)N×N迷宫路径求解a)4×4迷宫矩阵:可编辑

.01001011001110011011101111000110运行后,判定为有解迷宫,输出如下:运行后,判定为无解迷宫,输出如下:(对符号解释的放置)(对1墙的解释)b)7×7迷宫矩阵01001011011010010010110111111101011111010101110000110101101100100011011111101000000011011000001010运行后,判定为有解迷宫,输出如下:运行后,判定为无解迷宫,输出如下:可编辑.(1)N×M迷宫路径求解(N!=M)a)4×6迷宫矩阵010010010010000101101001100101101011011010111010运行后,判定为有解迷宫,输出如下:运行后,判定为无解迷宫,输出如下:b)5×3迷宫矩阵010010可编辑.010001001110111000010010运行后,判定为有解迷宫,输出如下:运行后,判定为无解迷宫,输出如下:2、自动迷宫(1)N×N迷宫路径求解5×5迷宫矩阵测试次数第一次第二次第三次生成迷宫可编辑.判断结果(2)N×M迷宫矩阵测试(N!=M)6*8迷宫矩阵测试次数第一次第二次第三次生成迷宫判断结果(二)广度优先搜索(BFS)测试--------多通路迷宫折取最优。可编辑.1、课本实例(P68测试用例1)输出迷宫输出结果2、自行设计手动输入迷宫测试迷宫全部路径展示可编辑.测试结果四、分析与探讨第三部分中,展示了程序所实现的各种情况。下面从程序源代码的角度来进一步分析各算法的时间复杂性。1、程序分析(1)手动生成迷宫算法:voidhand_maze(intm,intn){inti,j;cout<<endl;cout<<"请按行输入迷宫,0表示通路,1表示障碍:"<<endl;for(i=0;i<m;i++)for(j=0;j<n;j++){cin>>maze[i][j];}}可以看出,该算法的时间复杂度为T(N)=O(N2)。因此,这是一个线性算法,随着N的增加,算法的时间复杂度线性增加。(2)自动生成迷宫算法:voidautomatic_maze(intm,intn)//自动生成迷宫{inti,j;cout<<"\n迷宫生成中……\n\n";system("pause");可编辑.for(i=0;i<m;i++)for(j=0;j<n;j++)maze[i][j]=rand()%2;//随机生成0、1maze[0][0]=0;maze[i-1][j-1]=0;//首尾置为0,保证迷宫基本功能的实现}时间复杂性、度上,该算法同于手动生成迷宫算法,为T(N)=O(N2),也是一个线性算法,随着N的增加,算法的时间复杂性增加。(3)输出迷宫的星号路径算法:voidresult_maze(intm,intn){//打印输出迷宫的星号路径inti,j;printf("迷宫通路(用☆表示)如下所示:\n\t");for(i=0;i<m;i++){printf("\n");for(j=0;j<n;j++){if(maze[i][j]==0||maze[i][j]==2)printf("□");if(maze[i][j]==1)printf("■");if(maze[i][j]==3)printf("☆");}}}2、程序讨论(1)在程序编写的过程中,我们发现了很多问题及错误。例如,程序刚形成雏形时,经测试,程序只能实现课本要求的8*8的迷宫矩阵寻路,并且会出现寻路时跳出迷宫的情况。因此,程序员在此基础上,将迷宫矩阵数组由原来的maze[10][10]改为maze[N+2][M+2],并在相应函数上加以大量改动,最终使本程序实现了程序本身范围内的任意N*M迷宫矩阵组合的寻路求解。可编辑

.(2)在输出界面上,起初,仅仅是将最终结果展示,而无矩阵的规范输出,即如果使用者手输出迷宫时,并未整齐的输入迷宫,如果此时显示迷宫最后的路径,使使用者不易清晰的看出路径而难以理解。基于站在使用者的角度,加入了data函数,实现了数组、符号、数位路径、字符路径共同出现的最终界面,并在输出时添加相应的箭头指示,在输出的迷宫图时将最外层加入的围墙用“▲”表示,经多次修改,使得输出界面整洁大方。(3)在最开始判断搜索方向的时候,我们列出的八个方向并未按照一定顺序搜索,此时就会出现错误,后来采用一定顺序来完成迷宫的搜索,定设迷宫的入口在左上方,出口在右下方,所以采用顺时针方向比采用逆时针方向能最先找出最短路径。所以最后采用了顺时针的搜索方向,即沿北、东北、东、东南、南、西南、西、西北八个方向搜索。(4)在测试输出结果方面,出了一个困扰我们一阵时间的问题,在以5*7自动生成迷宫矩阵的测试过程中,几乎每隔几次都会出现一次这样类似的问题(见下图),在下图的输出界面以及字符展示界面中,经过简单的分析,便可以得出最后通往出口的道路,但在程序判断并输出路径后,在图中可以看到:五角星所标记的走过的路,在出口处出现了错误。但是此程序手动输入的时候不会出现任何错误,所以问题锁定在自动生成迷宫函数中。在此,我们列出当时的自动生成迷宫函数:可编辑.voidautomatic_maze(intm,intn){inti,j;cout<<"\n迷宫生成中……\n\n";system("pause");for(i=0;i<m;i++)for(j=0;j<n;j++)maze[i][j]=rand()%2;maze[0][0]=0;maze[i-1][j-1]=0;}将此问题反映给了程序员,在修改过程中,反复分析了各个函数后将问题锁定在自动生成函数处。观察此函数,发现“maze[i-1][j-1]=0;”是导致问题的最终所在。该语句的初衷是将迷宫矩阵中最初一个数判定为0,防止出现迷宫出口不通的情况。在这儿以5*7迷宫为例,经过分析,当跳出for循环时,i=5,j=6,而要改变的数是maze[5][7],而非maze[5][6],这也就是导致上图所示结果的原因,将该语句改为maze[m-1][n-1],问题解决。3、程序的前景展望及感想由于时间匆忙,还有很多功能未能实现。例如:自动生成迷宫时经常出现无解迷宫,加以改进的话,希望可以实现当自动生成的迷宫无解时,可以自动搜索直到出现有解迷宫;或者我们可以实现操作者可以自己手动输出迷宫路径等等。在整个程序设计的几天里,我们三个同学分工明确,共同设计讨论程序,经过多方改进与完善,最终完成了此次迷宫的设计。附录源代码可编辑

.#include<stdlib.h>#include<stdio.h>#include<iostream>usingnamespacestd;#defineN49//库中包含system("pause")和rand()函数//c语言里的库//定义为全局变量,这是迷宫数组的上线,可以自行修改#defineM49intX;intmaze[N+2][M+2];inthead=0,tail=0;//队列的头尾指针,初始值设为0structpoint//存放迷宫访问到点的队列结构体,包含列,行,序号{introw,col,predecessor;}queue[1200];voidhand_maze(intm,intn)//手动生成迷宫{inti,j;cout<<endl;cout<<"请按行输入迷宫,0表示通路,1表示障碍:"<<endl;for(i=0;i<m;i++)for(j=0;j<n;j++){cin>>maze[i][j];}}voidautomatic_maze(intm,intn)//自动生成迷宫{inti,j;cout<<"\n迷宫生成中……\n\n";system("pause");for(i=0;i<m;i++)for(j=0;j<n;j++)maze[i][j]=rand()%2;maze[0][0]=0;宫//随机生成0、1//将开始和结束位置强制为0,保证有可能出来迷maze[m-1][n-1]=0;}voiddata(intm,intn)可编辑

.{//当用户输入的不是规整的m行n列的迷宫,用来生成规则的数字迷宫inti,j;cout<<endl;cout<<"根据您先前设定的迷宫范围"<<endl;cout<<endl;cout<<"我们将取所输入的前"<<m*n<<"个数生成迷宫"<<endl;cout<<"\n数字迷宫生成结果如下:\n\n";cout<<"迷宫入口\n";cout<<"↓";for(i=0;i<m;i++){cout<<"\n";for(j=0;j<n;j++){if(maze[i][j]==0)cout<<"0";if(maze[i][j]==1)cout<<"1";}}cout<<"→迷宫出口\n";}voidprint_maze(intm,intn){//打印迷宫外壳inti,j,k;cout<<"\n字符迷宫生成结果如下:\n\n";cout<<"迷宫入口\n";cout<<"↓";cout<<endl;cout<<"▲";//生成上外壳for(k=0;k<n;k++){cout<<"▲";//这两个黑三角用来生成顶部外壳}for(i=0;i<m;i++){cout<<"\n";//生成左外壳cout<<"▲";for(j=0;j<n;j++){if(maze[i][j]==0)printf("□");if(maze[i][j]==1)printf("■");可编辑

.}cout<<"▲";//生成右外壳}cout<<endl;for(k=0;k<n;k++){cout<<"▲";}cout<<"▲\n";for(i=0;i<n;i++)//生成底部外壳{cout<<"";}cout<<"↓\n";for(i=0;i<n;i++){cout<<"";}cout<<"迷宫出口\n";}voidresult_maze(intm,intn)//这个是打印输出迷宫的星号路径{inti,j;cout<<"迷宫通路(用☆表示)如下所示:\n\t";for(i=0;i<m;i++){cout<<"\n";for(j=0;j<n;j++){if(maze[i][j]==0||maze[i][j]==2)//2是队列中访问过的点cout<<"□";if(maze[i][j]==1)cout<<"■";if(maze[i][j]==3)//3是标记的可以走通的路径cout<<"☆";}}}voidenqueue(structpointp)//迷宫中队列入队操作//先用再加,队列尾部加1{queue[tail]=p;tail++;}structpointdequeue()定义//迷宫中队列出队操作,不需要形参,因此用结构体可编辑

.{}head++;returnqueue[head-1];intis_empty(){//判断队列是否为空returnhead==tail;}voidvisit(introw,intcol,intmaze[51][51]){//访问迷宫矩阵中的节点structpointvisit_point={row,col,head-1};//head-1的作用是正在访问的这个点的序号为之前的点序号maze[row][col]=2;//将访问过的点位标记为2enqueue(visit_point);//入队}intpath(intmaze[51][51],intm,intn)//路径求解{X=1;//初始值定为1structpointp={0,0,-1};//定义入口节点if(maze[p.row][p.col]==1){//入口为1时,迷宫不可解cout<<"\n===============================================\n";cout<<"此迷宫无解\n\n";X=0;return0;}maze[p.row][p.col]=2;enqueue(p);//标记为已访问//将p入队列while(!is_empty()){p=dequeue();if((p.row==m-1)&&(p.col==n-1))//当行和列为出口时跳出break;//定义8个走位方向if((((p.row-1)>=0)&&((p.row-1)<m)&&((p.col+0)<n))&&(maze[p.row-1][p.col+0]==0))visit(p.row-1,p.col+0,maze);//北if((((p.row-1)>=0)&&((p.row-1)<m)&&((p.col+1)<n))&&(maze[p.row-1][p.col+1]==0))可编辑

.visit(p.row-1,p.col+1,maze);//东北if((((p.row+0)<m)&&((p.col+1)<n))&&(maze[p.row+0][p.col+1]==0))visit(p.row+0,p.col+1,maze);//东if((((p.row+1)<m)&&((p.col+1)<n))&&(maze[p.row+1][p.col+1]==0))visit(p.row+1,p.col+1,maze);//东南if((((p.row+1)<m)&&((p.col+0)<n))&&(maze[p.row+1][p.col+0]==0))visit(p.row+1,p.col+0,maze);//南if((((p.row+1)<m)&&((p.col-1)<n)&&((p.col-1)>=0))&&(maze[p.row+1][p.col-1]==0))visit(p.row+1,p.col-1,maze);//西南if((((p.row+0)<m)&&((p.col-1)<n)&&((p.col-1)>=0))&&(maze[p.row+0][p.col-1]==0))visit(p.row+0,p.col-1,maze);//西if((((p.row-1)>=0)&&((p.row-1)<m)&&((p.col-1)<n)&&((p.col-1)>=0))&&(maze[p.row-1][p.col-1]==0))visit(p.row-1,p.col-1,maze);//西北}if(p.row==m-1&&p.col==n-1){//如果当前矩阵点是出口点,输出路径cout<<"\n==================================================================\n";cout<<"迷宫路径为:\n";cout<<"出口"<<endl;cout<<""<<"↑"<<endl;printf("(%d,%d)\n",p.row+1,p.col+1);cout<<""<<"↑"<<endl;maze[p.row][p.col]=3;while(p.predecessor!=-1){//逆序将路径标记为3p=queue[p.predecessor];printf("(%d,%d)\n",p.row+1,p.col+1);cout<<""<<"↑"<<endl;maze[p.row][

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论