自己希望打实基础。当初看这个迷宫很是晕乎,所以现在拿出来写一下。以前一直没能认真地研究一下数据结构,现在当作是还债把。我是菜鸟不解释。。。
迷宫的基础算法在严蔚敏老师写的数据结构(C语言版)已经描述过了。主要是用栈的特性来保存在迷宫之中走过的路径,走到死胡同后再用栈弹出栈顶,再将指针指到栈顶。这种穷尽的算法有点笨(额 电脑就是这样),貌似也有一些更好的搜索路径,不过现在俺还没接触学习到0 0 ,比如A*算法。记得老师讲过,不过很快就还给老师了。哈哈~~废话少说,现在就是用面向对象的思想来完成走迷宫的这样一个程序。(第一次写,不好勿怪,纯属菜鸟交流~~)
首先可以认为迷宫分为许多个划分为许多个小格子的有序集合(乱扯的。。。),迷宫就是这些小格子组成的。可以认为它们有坐标,x,y;给一个标记Flag来指示该格子是否已经走过(默认为false);因为迷宫之间还应该有一些不能走的地方,称之为墙,那么在用一个IsWall来标识该格子是否是墙。哦,还应该给这个格子标记上行走的方向(这个方向我想应该做为一个枚举)。下面是代码:
//迷宫中的一个小格子 public class MazeElem { //这是方向,初始的方向应该是None~木有方向~ public Direction dir = Direction.None; //构造函数 public MazeElem(int x,int y,bool isWall) { _x=x; _y=y; _isWall=isWall; }
private int _x; private int _y;
//flag的默认值应该是false,假定所有的格子都没有走过 private bool _flag=false; private bool _isWall; //X坐标,作为属性了 public int X { get { return _x; } set { _x = value; } } //Y坐标,也成属性了0 0 public int Y { get { return _y; } set { _y = value; } } //是否墙 public bool IsWall { get{return _isWall;} set{_isWall=value;} } //是否走过 public bool Flag { get { return _flag; } set { _flag = value; } } }
好了,格子类写好了。应该挺容易理解的把。这些都是基础,俺只是想打一下自己的基础~我们再写一个方向的枚举。一个格子的方向很容易想到:东西南北对应(右左下上),除了这些,还应该有这两个:一个是初始的方向,(一开始方向的初始值我想设置为null的,结果报错了。。。。切记。。枚举是值类型。。)还有当这个格子各个方面都无路可走的时候(人若如此只能感叹悲催...),我以为要做一个标记,那就设置NoDir。
//方向枚举 public enum Direction { ///
/// 初始值,木有方向 /// None, ////// 北/上 /// North, ////// 南/下 /// South, ////// 西/左 /// West, ////// 东/右 /// East, ////// 无放向可走 /// NoDir }
接下来开始写这个迷宫类。可以认为迷宫是一个n X n 个迷宫格子的集合,所以设置一个迷宫格子的的二维数组MazeElem[,];为了指示当前行走的迷宫格子,设置一个MazeElem类型cur变量;当然还要设置一个迷宫的出口end(这里简化一下吧,默认出口就是这个迷宫右下角的那个小格子,入口就是迷宫左上角的格子。。。cur初始值为入口处。。。)。还有栈是必须的,主要是用来保存走过的路径,.NET已经提供一个Stack类,不需要我们自己写(想想用List来写栈好像也不是很难,这是看到某位大神的思路的)。嗯,还要用一对xy变量来保存这个迷宫的大小,方便后面的比较。
public class Maze { //很多的格子 public MazeElem[,] maze; //当前格子 MazeElem cur; //这是出口的那个格子 MazeElem end; //用来保存路径的栈 Stack
path = new Stack (); //这两个变量是用来保存迷宫的大小的 int x, y; public Maze() { } //构造函数,这个是最简单木有任何墙出现的空白迷宫。。 public Maze(int x,int y) { x = x; y = y; maze=new MazeElem[x,y]; //初始化 for(int i=0;i 0?true:false); //cur默认为左上角的格子,出口默认为右下角的格子 cur=maze[0, 0]; end = maze[x - 1, y - 1]; }
有一些方法还没写出来。先理清一下走迷宫的算法:
判断当前格子是否已经走过,如果没走过(flag=false):设置为已经走过flag=true,该格子入栈,然后选定一个方向尝试走其他格子;如果走过了:检查该格子的方向,如果不是NoDir(NoDir说明四个方向都尝试过了),选定一个方向尝试走其他格子;如果是NoDir,那么将栈顶弹出,并将cur指向栈顶的格子。重复操作直道格当前格子指向迷宫的出口。选定一个方向尝试走其他格子的方法流程是这样的:先切换这个格子的方向,然后根据切换后的方向进行尝试,如果该方向的格子无法走过,那么继续选定一个方向尝试走其他格子,如果可以走过,那么将cur指向该格子。切换这个格子的方向,这个就比较简单~我们顺时针方向切换,开始的是East,向右走(dir为None,我就返回East;如果是East,那我返回South;如果是South,那返回West;如果是West,返回North;如果是North,那返回NoDir)。额,应该说得稍微清楚些把?具体看代码~一下都是Maze里面的方法,想复制的童鞋别复制错地方了0 0
private void CurGoNextStep(MazeElem me) { switch (SwitchDirection(me)) { case Direction.East: if (me.Y + 1 < y) { MazeElem next = maze[me.X, me.Y+1]; //不能为墙,是墙你会走过去么?而且也不能是已经走过的,好马不吃回头草 if (!next.IsWall && !next.Flag) { //next.Flag = true; cur = next; } //不行就换个方向把. else { CurGoNextStep(me); } } else { CurGoNextStep(me); } break; case Direction.South: if (me.X + 1
0) { MazeElem next = maze[me.X, me.Y - 1]; if (!next.IsWall && !next.Flag) { //next.Flag = true; cur = next; } else { CurGoNextStep(me); } } else { CurGoNextStep(me); } break; case Direction.North: if (me.X - 1 >0) { MazeElem next = maze[me.X - 1, me.Y]; if (!next.IsWall && !next.Flag) { //next.Flag = true; cur = next; } else { CurGoNextStep(me); } } else { CurGoNextStep(me); } break; case Direction.NoDir: //无路可走了已经 break; } } //转换方向 private Direction SwitchDirection(MazeElem me) { switch (me.dir) { case Direction .None: me.dir = Direction.East; return Direction.East; break; case Direction.East: me.dir = Direction.South; return Direction.South; break; case Direction.South: me.dir = Direction.West; return Direction.West; break; case Direction.West: me.dir = Direction.North; return Direction.North; break; case Direction.North: me.dir = Direction.NoDir; return Direction.NoDir; break; } return Direction.None; } //开始走迷宫 public void Start() { while (cur != end) {
//如果没走过 if (!cur.Flag) { //留下痕迹 cur.Flag = true; //节点入栈 path.Push(maze[cur.X,cur.Y]); //换方向走
CurGoNextStep(cur); } else {
//如果不是无路可走的情况 if (cur.dir != Direction.NoDir) CurGoNextStep(cur); else//无路可走了~~那就回头... { if (path.Count != 0) //栈顶弹出 path.Pop(); if (path.Count != 0) { //更新cur cur = path.Peek(); } } } } }
就这么多吧。可以测试一下:
//用数组简单表示一个迷宫,大于0则表示该格子为墙 int[,] array = new int[5, 5] { {0,0,1,0,0 }, {0,1,0,0,0}, {0,1,0,0,0},{ 0,1,0,0,0},{0,0,0,1,0}};
大概是下面这样的图~~这编辑器有点蛋疼,直接用表格画了(黑色表示墙),左上角和右下角分别为入口和出口(黄色的....)
//构造迷宫 Maze m = new Maze(array); //开始走迷宫 m.Start(); //打印出迷宫中走的方向 if (m.maze != null) { int tmp = 0; foreach (MazeElem me in m.maze) { tmp++; Console.Write(me.dir +" "); if (tmp % 5 == 0) Console.WriteLine(); } }
结果如下:感觉面向对象是好理解些,不过我不是很能够体现出面向对象的好处
完~~欢迎提意见....