上一节链接:https://blog.csdn.net/qq_40163148/article/details/96993725
上次我们采用递归的深度优先遍历走迷宫,那么今天我们使用非递归的深度优先遍历来试一下我们的走迷宫
1.我们都知道非递归的深度优先遍历我们需要使用一个数据结构叫做栈stack一种先进后出的数据结构
2.非递归的深度优先遍历和我们的深度优先遍历的逻辑是一样的:首先我们先让入口坐标入栈,访问标记置true,然后当栈不为空时开始循环每出栈一个元素就对其对应坐标进行渲染,然后当遇到出口时寻找路径(和递归不一样栈不会一步一步回溯只会一下跳跃回溯所以我们不能使用原来的方法求最后的解),跳出循环,不然就遍历左上右下四个方向当坐标合法,没有被访问过,并且是ROAD的情况下坐标入栈访问位入栈
数据层
比起上一节我们加入一个boolean[][] solved的变量用来记录最后的解具体的思路是当我们遇到出口时我们开始从出口坐标回溯它的上一个坐标即就是它的中心坐标(左上右下)我们就找到了最终的路径详细过程我们会在控制层讲解
package com.lipengge.nonrecursivedfsmaze.data;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class MazeData {
//迷宫是由字符数组组成的我们可以使用char数组
private char[][] maze;
private int entranceX,entranceY;
private int exitX,exitY;
public boolean[][] path;
public boolean[][] visited;
public boolean[][] solved;
public int[][] distance={{0,-1},{1,0},{0,1},{-1,0}};
public int getEntranceX() {
return entranceX;
}
public void setEntranceX(int entranceX) {
this.entranceX = entranceX;
}
public int getEntranceY() {
return entranceY;
}
public void setEntranceY(int entranceY) {
this.entranceY = entranceY;
}
public int getExitX() {
return exitX;
}
public void setExitX(int exitX) {
this.exitX = exitX;
}
public int getExitY() {
return exitY;
}
public void setExitY(int exitY) {
this.exitY = exitY;
}
public MazeData(String fileName) {
readFile(fileName);
}
//获取数组的行
public int getMazeL(){
return maze.length;
}
//获取数组的列
public int getMazeW(){
return maze[getMazeL()-1].length;
}
//获取数组的元素
public char getMazeElement(int x,int y){
if(isIllegal(x,y)){
throw new IllegalArgumentException("数组越界");
}
return maze[x][y];
}
//判断数组是否越界
public boolean isIllegal(int x,int y){
return !(x>=0&&x<getMazeL()&&y>=0&&y<getMazeW());
}
//读取文件
public void readFile(String fileName){
BufferedReader buf=null;
try{
File file=new File(fileName);
if(!file.exists()){
throw new IllegalArgumentException("文件不存在");
}
buf=new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(file))));
String[] num=buf.readLine().split(" ");
int r=Integer.parseInt(num[0]);
int c=Integer.parseInt(num[1]);
maze=new char[r][c];
path=new boolean[r][c];
visited=new boolean[r][c];
solved=new boolean[r][c];
for(int i=0;i<r;i++){
maze[i]=buf.readLine().toCharArray();
}
}catch(Exception e){
e.printStackTrace();
}finally{
if( buf!=null){
try {
buf.close();
} catch (IOException e) {
}
}
}
}
//输出数组
public void print(){
System.out.println(getMazeL()+" "+getMazeW());
for(int i=0;i<getMazeL();i++){
for(int j=0;j<getMazeW();j++){
System.out.print(getMazeElement(i, j));
}
System.out.println();
}
}
// public static void main(String[] args) {
// MazeData maze=new MazeData("maze_101_101.txt");
// maze.print();
// }
}
视图层
针对上述改进我们视图层也要进行一些小的改进就是当solved[x][y]为true时我们设置颜色为红色
```package com.lipengge.nonrecursivedfsmaze.view;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.lipengge.dfsmaze.util.Constant;
import com.lipengge.nonrecursivedfsmaze.data.MazeData;
import com.lipengge.randomquestion.util.AlgoVisHelper;
public class AlgoFrame extends JFrame{
/**
*
*/
private static final long serialVersionUID = -3035088527551930125L;
private int canvasWidth;//画布的宽
private int canvasHeight;//画布的长
private MazeData data;
public int getCanvasWidth() {
return canvasWidth;
}
public int getCanvasHeight() {
return canvasHeight;
}
public void render(MazeData data){
this.data=data;
repaint();
}
public AlgoFrame(String title,int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth=canvasWidth;
this.canvasHeight=canvasHeight;
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);//关闭可拖动
setVisible(true);
AlgoCanvas algoCanvas = new AlgoCanvas();
setContentPane(algoCanvas);
pack();
}
//设置面板JPanel
class AlgoCanvas extends JPanel{
/**
*
*/
private static final long serialVersionUID = -6056196800598676936L;
private AlgoCanvas(){
//开启双缓存
super(true);
}
@Override
//在面板上绘制内容
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d=(Graphics2D) g;
AlgoVisHelper.setRenderingHints(g2d);//设置抗锯齿
AlgoVisHelper.setStrokeWidth(g2d, 1);//设置边的宽度为5
int w=canvasWidth/data.getMazeL();
int h=canvasHeight/data.getMazeW();
for(int i=0;i<data.getMazeL();i++){
for(int j=0;j<data.getMazeW();j++){
if(data.path[i][j]){
g2d.setColor(Color.blue);
}else if(data.getMazeElement(i, j)==Constant.WALL){
g2d.setColor(Color.BLACK);
}else if(data.getMazeElement(i, j)==Constant.ROAD){
g2d.setColor(Color.WHITE);
}
if(data.solved[i][j]){
g2d.setColor(Color.red);
}
AlgoVisHelper.fillReactangle(g2d,w*j,h*i,w,h);
}
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(canvasWidth,canvasHeight);
}
}
}
控制层
控制层是我们算法的核心我们刚才说过自己讲解findpath那么我们记录坐标需要自定义一个类position,里面有x,y坐标还记录了一个position变量用来链式的记录当前左边的路径其实在我们进行四个方向的入栈时我们还做了一件事就是把当前坐标的上一个坐标也放进了position里所以我们其实已经记录了所有遍历过路径的路径链只不过我们在找到出口时只回溯出口的那条而已
这次我们在setData()函数里设置了出口的坐标
我们在非递归方法里设置了一个solved的布尔变量当遇到出口时置为true如果当循环结束时solved为false说明迷宫无解
package com.lipengge.nonrecursivedfsmaze.controller;
import java.awt.EventQueue;
import java.util.Stack;
import com.lipengge.dfsmaze.util.Constant;
import com.lipengge.nonrecursivedfsmaze.data.MazeData;
import com.lipengge.nonrecursivedfsmaze.model.Position;
import com.lipengge.nonrecursivedfsmaze.view.AlgoFrame;
import com.lipengge.randomquestion.util.AlgoVisHelper;
public class AlgoVisualizer {
private AlgoFrame frame;
private MazeData data;
private int screenWidth;
private int screenHeight;
public AlgoVisualizer(String fileName){
data=new MazeData(fileName);
this.screenWidth=data.getMazeL()*Constant.BLOCK;
this.screenHeight=data.getMazeW()*Constant.BLOCK;
//初始化视图
EventQueue.invokeLater(()->{
frame=new AlgoFrame("maze",screenWidth,screenHeight);
new Thread(()->run()).start();
});
}
//播放动画
public void run(){
setData(-1,-1,false);
nonrecursived();
setData(-1,-1,false);
}
private void setData(int x,int y,boolean isPath) {
data.setEntranceX(1);
data.setEntranceY(0);
data.setExitX(data.getMazeL()-2);
data.setExitY(data.getMazeW()-1);
if(!data.isIllegal(x, y)){
data.path[x][y]=isPath;
}
frame.render(data);
AlgoVisHelper.pause(0);
}
//非递归深度优先遍历走迷宫
public void nonrecursived(){
Stack<Position> stack=new Stack<>();
stack.push(new Position(data.getEntranceX(),data.getEntranceY()));
data.visited[data.getEntranceX()][data.getEntranceY()]=true;
boolean isSolved=false;
while(!stack.isEmpty()){
Position position=stack.pop();
setData(position.getX(),position.getY(),true);
//找到出口则跳出循环
if(position.getX()==data.getExitX()&&position.getY()==data.getExitY()){
isSolved=true;
findPath(position);
break;
}
for(int i=0;i<4;i++){
int newX=position.getX()+data.distance[i][0];
int newY=position.getY()+data.distance[i][1];
if(!data.isIllegal(newX,newY)&&data.getMazeElement(newX,newY)==Constant.ROAD&&!data.visited[newX][newY]){
stack.push(new Position(newX,newY,position));
data.visited[newX][newY]=true;
}
}
}
if(!isSolved){
System.out.println("迷宫无解");
}
}
//寻找最终路径
public void findPath(Position position){
data.solved[position.getX()][position.getY()]=true;
Position prev=position.cur;
while(prev!=null){
data.solved[prev.getX()][prev.getY()]=true;
prev=prev.cur;
}
}
public static void main(String[] args) {
new AlgoVisualizer("maze_101_101.txt");
}
}
position类
package com.lipengge.nonrecursivedfsmaze.model;
public class Position {
private int x;//点的x坐标
private int y;//点的y坐标
public Position cur;
public Position(int x, int y,Position cur) {
this.cur=cur;
this.x = x;
this.y = y;
}
public Position(int x, int y) {
this(x,y,null);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
绘制帮助类
package com.lipengge.minesweeper.util;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.ImageIcon;
public class AlgoVisHelper {
private AlgoVisHelper(){}
//画圆描边的辅助类
public static void strokeCircle(Graphics2D g,int x,int y,int r){
//圆数据的闭包
Ellipse2D circle = new Ellipse2D.Double(x-r,y-r,2*r,2*r);
g.draw(circle);
}
//画圆填充的辅助类
public static void fillCircle(Graphics2D g,int x,int y,int r){
Ellipse2D circle = new Ellipse2D.Double(x-r,y-r,2*r,2*r);
g.fill(circle);
}
//画矩形的辅助类
public static void strokeReactangle(Graphics2D g,int x,int y,int width,int height){
Rectangle2D reactangle = new Rectangle2D.Double(x,y,width,height);
g.draw(reactangle);
}
//填充矩形的辅助类
public static void fillReactangle(Graphics2D g,int x,int y,int width,int height){
Rectangle2D reactangle = new Rectangle2D.Double(x,y,width,height);
g.fill(reactangle);
}
//设置画笔颜色
public static void setColor(Graphics2D g,Color color){
g.setColor(color);
}
//设置边的宽度和平滑性
public static void setStrokeWidth(Graphics2D g,int w){
//BasicStroke.CAP_ROUND把定点变为圆弧半径为画笔一半
//BasicStroke.JOIN_ROUND把线条连接点变为弧形
g.setStroke(new BasicStroke(w,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
}
//设置抗锯齿
public static void setRenderingHints(Graphics2D g){
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.addRenderingHints(hints);
}
public static void pause(long millsecond){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//画图
public static void drawImage(Graphics2D g,String imageUrl,int x,int y,int x1,int y1){
ImageIcon imageIcon=new ImageIcon(imageUrl);
Image image=imageIcon.getImage();
g.drawImage(image,x, y,x1,y1,null);
}
}
常量类
package com.lipengge.nonrecursivedfsmaze.util;
public class Constant {
public static final int BLOCK=10;
public static final char WALL='#';
public static final char ROAD=' ';
}
效果图