欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

迷宫(基于深度优先遍历和广度优先遍历实现)

程序员文章站 2022-05-21 22:57:20
...

先将文件拖到项目的根目录下
创建一个主函数

public class Main {
	public static void main(String[] args) {
     //根据文件名创建迷宫数据对象
	AlgoVisualizer maze = new AlgoVisualizer("maze_101_101.txt");
	}
}

创建一个MazeData类
public class MazeData {
public static final char ROAD = ’ ';//定义路为空格
public static final char WALL = ‘#’;//定义墙为#
private int N; //定义成成员变量以便再次进行访问,外界间接访问
private int M;
private char[][] maze;//构建好迷宫数组
public boolean[][] visited; //定义一个记录访问状态数组
public boolean[][] path; //定义一个路径数组
private int entranceX , entranceY;//定义迷宫入口
private int exitX , exitY; //定义迷宫出口
public MazeData(String filename) { //传入文件名

if(filename == null) {   //先判空
				throw new IllegalArgumentException("文件名为空");//为空时抛出异常
			}
			//根据文件名创建该文件对象
			File file = new File(filename);	//创建该文件对象
			Scanner input = null;	//先默认初始化为空
			if(!file.exists())	
				throw new IllegalArgumentException("文件不存在");
			//调用FileInputStream时可能会出错,所以用+++try-catch
			try { //开始读取文件
				FileInputStream fis = new FileInputStream(file);	//创建该文件对象
				input = new Scanner(fis);		//接收该文件
				
				String nmline = input.nextLine();//读取文件内容的第一行
				String[] nm = nmline.split(" ");	//将第一行的内容进行划分
				N = Integer.parseInt(nm[0]);		//定义好迷宫的宽
				M = Integer.parseInt(nm[1]);
				maze = new char[N][M];		//设置好迷宫大小
				visited=new boolean[N][M];//设置访问数组
				path=new boolean[N][M];	//设置路径数组
				for (int i = 0; i < N; i++) {		//嵌套循环对文本内容进行判断并读取到数组中
					String line = input.nextLine();  //将每一行的内容读取到line字符串中
					if(line.length() != M)			//判断读取到该行长度是否与数组的长度相等,文件是否损坏
						throw new IllegalArgumentException("文件内容错误");//不相等抛出异常
					for (int j = 0; j < M; j++) {
						maze[i][j] = line.charAt(j);	//依次将指定角标元素存入到数组中去
					}
				}//for:i
				entranceX = 1; entranceY = 0;		//将迷宫的入口和出口确定
				exitX = N - 2 ; exitY = M - 1;
			} catch (Exception e) {			//捕获异常
				e.printStackTrace();
			}finally {
				if(input !=  null)		//如果读取到的内容不为空时
					input.close();		//关闭I/0流
			}
			
		}
              //构造函数
		//对指定位置进行判断是否在指定范围(迷宫)内
		public boolean inArea(int x , int y) {
			return x >= 0 && x < N && y >= 0 && y < M;
		}
		//获取当前位置的信息
		public char getMaze(int x , int y) {
			 if(!inArea(x, y))	//不在范围内时
				throw new IllegalArgumentException("位置出错");
			return maze[x][y];
		}
		//因为N,M已经private所以间接访问
		public int getN() {
			return N;
		}
		public int getM() {
			return M;
		}
		
		public int getEntranceX(){
			return entranceX;
		}
		public int getEntranceY(){
			return entranceY;
		}
		public int getExitX() {
			return exitX;
		}
		public int getExitY() {
			return exitY;
		}
	}

迷宫的数据已经创建完,将生成的数据转化成图形化界面
导入AlgoFrame(窗口,绘画)、AlgoVisualizer(初始化数据、初始化视图)、AlgoVisHelper(工具类)
1、在AlgoVisualizer中
原来传入的宽高改成文件名,通过文件名创建data数据

public AlgoVisualizer(String fileName){
    	//初始化数据
    	data = new MazeData(fileName);
    	int sceneWidth = data.getM() * BLOCK_SIDE;//根据格子的大小决定屏幕的宽高
        int sceneHeight = data.getN() * BLOCK_SIDE;

设置动画逻辑,先直接绘制,之后具体实现动画逻辑

private void run(){
   frame.render(data); 
}

2、AlgoFrame中
//设置自己的数据

 private MazeData data;
    public void render(MazeData data){//接收数据
        this.data = data;
        repaint();//画布进行重绘
    }

具体绘制
//具体绘制

int w = canvasWidth / data.getM();//每一个格子的宽
   int h = canvasHeight / data.getN();
   for (int i = 0; i < data.getN(); i++) {
    	for (int j = 0; j < data.getM(); j++) {
			if((data.getMaze(i, j) == MazeData.WALL)){//如果是墙时设置成蓝色
				AlgoVisHelper.setColor(g2d, AlgoVisHelper.LightBlue);
			}else {//是路设置成白色
		         	AlgoVisHelper.setColor(g2d, AlgoVisHelper.White);
				}
			if(data.path[i][j] == true)
				AlgoVisHelper.setColor(g2d, AlgoVisHelper.Orange);
				AlgoVisHelper.fillRectangle(g2d, j * w, i * h, w, h);//画一个矩形
				}
			}

1、深度优先遍历-递归方式

if(!go(data.getEntranceX(), data.getEntranceY())) {
    		 throw new IllegalArgumentException("迷宫无解");
    	    }

 
 private boolean go(int x , int y ) {
    	if(!data.inArea(x, y))
    		throw new IllegalArgumentException("遍历越界");
    	data.visited[x][y] = true;
    	setData(x,y,true);//标记为路
    	if(x == data.getExitX() && y == data.getExitY()) {
    		return true;//找到后跳出
    	}
    	for (int i = 0; i < d.length; i++) {
		 	int newX = x + d[i][0];
			int newY = y + d[i][1];
			//非访问/没越界/
			if(data.inArea(newX, newY) && 
				!data.visited[newX][newY] &&
					data.getMaze(newX, newY) == MazeData.ROAD) {
				if(go(newX, newY)) {
					return true;
				}
			}//if:i
			
		}//for
    	setData(x, y,false);
		return false;
    }

标记路
 private void setData(int x ,int y, boolean isPath) {
    	if (data.inArea(x, y)) {
			data.path[x][y] = isPath;//标记为路
		}
    	frame.render(data);//画布接收数据重新绘制
    	AlgoVisHelper.pause(1);//设置探路速度
    }

2、深度优先遍历-非递归方式>栈

定义一个Position类
public class Position {
	private int x,y;
	private Position pre;//当前位置的前一个结点
	public Position(int x,int y,Position pre) {
		this.x=x;
		this.y=y;
		this.pre=pre;
	}
	public int getX() {return x;}
	public int getY() {return y;}
	public Position getPre() {return pre;}
}


 Stack<Position> stack=new Stack<Position>();
        Position entrance=new Position(data.getEntranceX(),data.getEntranceY(),null);//入口的前者为null
        stack.push(entrance);//入口结点进栈
        data.visited[entrance.getX()][entrance.getY()]=true;//当前进入的结点标记已经访问
        boolean isSolved=false;
        while(!stack.empty()) {	//如果栈为空 仅仅表示所有的可能路径查找完毕
        	Position curPosition=stack.pop(); //当前结点弹栈
        	setData(curPosition.getX(),curPosition.getY(),true);//更新路径数据
        	if(curPosition.getX()==data.getExitX()&&curPosition.getY()==data.getExitY()) {
        		//找到出口
        		isSolved=true;
        		findPath(curPosition);//找路径
        		break;
        	}
        	for(int i=0;i<d.length;i++) {
        		int newX=curPosition.getX()+d[i][0];//定义新坐标
        		int newY=curPosition.getY()+d[i][1];
        		if(data.inArea(newX, newY)
        				&&data.getMaze(newX, newY)==MazeData.ROAD
        				&&!data.visited[newX][newY]) {
        			//在范围&&是个路&&未能访问
        			stack.push(new Position(newX, newY, curPosition));//新结点入栈
        			data.visited[newX][newY]=true;//设置已访问状态
        		}
        	}
        }
        //while结束有俩种可能,正常跳出和非正常跳出
        if(!isSolved) {//没有被解决
        	throw new IllegalArgumentException("找不到解决方案");
        }

定义一个findPath类寻找最终路径
        private void findPath(Position des) {//当前结点为des
    	Position cur=des;//cur移动寻找
    	while(cur!=null) {
    		data.result[cur.getX()][cur.getY()]=true;
    		cur=cur.getPre();
    	}
    }

3.广度优先遍历-队列
广度优先遍历所寻找的路径是最短的(一层一层的往下走)

LinkedList<Position> queue=new LinkedList<Position>();//双端队列  
      Position entrance=new Position(data.getEntranceX(),data.getEntranceY(),null);//入口的前者为null   
      queue.addLast(entrance);//尾插法 
      data.visited[entrance.getX()][entrance.getY()] =true;//标记已访问
      boolean isSolved=false;//默认没有被解决
       while(queue.size()!=0) {
    	  Position curPosition =queue.removeFirst();//出队第一个元素并给curPosition接收
    	  setData(curPosition.getX(),curPosition.getY(),true);
    	  if(curPosition.getX()==data.getExitX()&&curPosition.getY()==data.getExitY()) {
    		 isSolved=true;
    		  findPath(curPosition);
    		  break;
    	  }
    	  for(int i=0;i<d.length;i++) {
      		int newX=curPosition.getX()+d[i][0];//定义新坐标
      		int newY=curPosition.getY()+d[i][1];
      		if(data.inArea(newX, newY)
      				&&data.getMaze(newX, newY)==MazeData.ROAD
      				&&!data.visited[newX][newY]) {
      			//在范围&&是个路&&未能访问
      			queue.addLast(new Position(newX, newY, curPosition));//新结点入队
      			data.visited[newX][newY]=true;//设置已访问状态
      		}
      	}
       }