算法设计大作业
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
迷宫问题解决
摘要:迷宫求解是一个古老的游戏,要在迷宫中找到出口,需要经过一连串的错误尝试才能找到正确的路径,有的时候甚至找不到路径。类似于给定一个m*n的矩形网格,设其左上角为起点S。一辆汽车从起点出发驶向右下角终点T。在若干网格处设置了障碍,表示该网格不可到达。设计一个算法,求汽车从起点S出发到达终点T的一条路线。用计算机求解这个问题时,我们通常采用的是回溯方法,即从入口出发,顺某方向向前探索,若能走通,则继续往前走;否则沿原路退回。换一个方向再继续探索,直至所有可能的通路都探索到为止。为了保证在任何位置上都能沿原路退回,显然需要用一个后进先出的结构来保存从入口到当前位置的路径。因此,在求迷宫通路的算法中应用“栈”也就是自然而然的事。当然还有其他的方法来解决,例如顺序表,深度优先遍历,广度优先遍历等。
1设计总体方案
1.1总体方案
走迷宫问题的走迷宫的过程可以模拟为一个搜索的过程:每到一处,总让它按东、南、西、北、4个方向顺序试探下一个位置;如果某方向可以通过,并且不曾到达,则前进一步,在新位置上继续进行搜索;如果4个方向都走不通或曾经到达过,则退回一步,在原来的位置上继续试探下一位置。
每前进或后退一步,都要进行判断:若前进到了出口处,则说明找到了一条通路;若退回到了入口处,则说明不存在通路。
1.2主要设计思路
用一个字符类型的二维数组表示迷宫,输入值的范围是0,1,其中0表示路径,1为非路径(即障碍),输入的矩阵大小和矩阵的内容是靠手动输入。设计一个模拟走迷宫的算法,为其寻找一条从入口点到出口点的通路。
解决迷宫问题,面对的第一个问题是迷宫的表示方法。假定用n*m矩阵来描述迷宫。左上角为入口,右下角为出口。n和m分别表示迷宫的行数和列数。矩阵中,0表示可通行的路径,1代表障碍。
如图1-1表示4*6的迷宫矩阵表示。
如果现在的位置(i,j),则下一步有:上、下、左、右四种走法,如图1-2表示。
迷宫内部的位置有四种移动的位置:上、下、左、右。而迷宫的边界位置有两种或三种可能的移动。为避免在处理内部和边界位置是存在差异,可在迷宫的周围增加一圈障碍物,如图1-3表示。
增加一圈障碍物之后,原迷宫中的每个位置都可以有四种移动方向。而且可以使程序不必去处理边界条件,从而大大简化代码的设计。这种简化是以稍稍增加数组amze 的空间为代价的。
分别用x和y来指定每个迷宫位置的行坐标和列坐标。可以定义一个类class T 来表示迷宫的位置。
class T //定义描述迷宫中当前位置的结构类型
{
public:
int x; //x代表当前位置的行坐标
int y; //y代表当前位置的列坐标
int dir; //0:无效,1:右,2:下,3:左,4:上
};
当输入一个二维数组时,每次输入的元素都存放在一个栈里面。所以定义一个栈Stack用于存放二维数组元素。这里用到栈,栈是限定尽在表位进行插入和删除的线性表。对于栈来说,允许进行插入和删除的一段,称为栈顶;不允许插入和删除的另一端,则成为栈底。在这个问题中主要运用了栈的各种基本操作,例如构造空栈,判断栈是否为空,入栈操作,出栈操作等等。这里是栈的一个重要应用,就是实现递归。
class Stack
{public:
Stack(); //构造函数,置空栈
~Stack(); //析构函数
void Push(T e); //把元素data压入栈中
T Pop(); //使栈顶元素出栈
T GetPop(); //取出栈顶元素
void Clear(); //把栈清空
bool empty(); //判断栈是否为空,如果为空则返回1,否则返回0
private:
LinkNode *top; }; //指向第一个结点的栈顶指针
调试结果如图1-4:
图1-4 迷宫和矩阵的建立
如果位置(i,j)的下一步的四个方向都是可以走的,假设出发的先后顺序是向上.向下.向左.向右。每个结点都是按照先后顺序来决定下一个结点的方向。一旦做出了决定,就需要知道下一个位置的坐标。可通过保留一个如图1-5所示的偏移量表来计算这些坐标。可以把向右.向左.向下.向上移动分别编号为0.1.2.3。在图1-3中,move[i].x和move[i].y分别给出了从当前位置沿方向移动到下一个相连位置时,x和y坐标的增量。
因此假设现在的位置是(2,3),则其右边相连位置坐标的行坐标为2+move[0].x=2,列坐标为3+move[0].y=4.
定义一个主函数int main(),用于调用其他函数实现函数的嵌套调用,实现整个程序。这里运用的二维指针我是参考书上的。
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;}
调试结果:
图1-6 搜索迷宫完成界面
时间算法复杂度:
O(n²)(不确定,我不太会算这个。)
对相关问题的思考:
这个迷宫问题的算法中,要在开始设置迷宫的大小。在探索迷宫路线的过程中,是通过不断的尝试来得到最终的结果,由于不能对已经设定为可走路径进行返回,所以在这个算法中有时候可能不能得到走出迷宫的路径。
总结
本次设计进展比较顺利,如期完成,并且达到了预先的设计要求,完全贯彻和执行了设计的总体方案。对于迷宫求解的模拟实现比较成功。然而,限于时间和水平,这个设计还有很多的不足之处。
迷宫问题是一个古老的问题,要在迷宫中找到出口,需要经过一连串的的错误尝试才能找到正确的路径,有时甚至找不到路径。而且这里有很多方法可以完成迷宫问题,例如顺序表,深度优先遍历,广度优先遍历等,但是我写不出程序。于是参考了《数据结构》书和我以前的一些设计,所以我们这里用的是栈。在这个问题中主要运用了栈的各种基本操作,例如构造空栈,判断栈是否为空,入栈操作,出栈操作等等。
通过这次的作业,我又学会了一种解决迷宫问题的方法,我很高兴。当让在完成设计过程中,我也遇到了很多难题,比如用什么办法解决问题,怎样创建调用栈等等,但在再次复习了当时所学的《C++》,《数据结构》等课程后,发现这些问题还是可以解决的,而且解决的方法不止一种。在这里我参考的最多的是《数据结构》中“栈的应用”那一节的知识,它给我了很大帮助。
本程序虽然简短,但是却有很多难点出现。首先是对基类的调试。为了减少调试的难度,我先调试了一下类的程序。刚开始是在主函数里面直接对程序赋值,发现这将大大限制程序的可重用性,而且无法灵活运用。在指导老师的帮助下,我将程序改成用键盘直接输出,这样的话方便实现。
当然,在我遇到苦难时候,老师和同学的帮助也是很大的。他们使我进步。也让我体会到了成功的来之不易,只有真正付出过才有满意的收获。在此,我诚心的对所有帮助过我的老师、学长、同学们说一句:谢谢!