迷宫求解算法一直是算法学习的经典,实现自然也是多种多样,包括动态规划,递归等实现,这里我们使用穷举求解,加深对栈的理解和应用。迷宫求解算法可以抽象为图的遍历问题。在图的遍历过程中通常由深度优先遍历(DFS
)和广度优先遍历(BFS
)两种。此例我们采用深度优先遍历,当然,在不同的情景之下,不同的优先遍历算法执行效率会有很大的差异,需要根据不同的使用环境选用合适的遍历算法。
深度优先遍历
深度优先遍历从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个新的顶点进行访问,重复此步骤,直到所有结点都被访问完为止。
广度优先遍历
广度优先遍历从某个顶点出发,首先访问这个顶点,然后找出这个结点的所有未被访问的邻接点,访问完后再访问这些结点中第一个邻接点的所有结点,重复此方法,直到所有结点都被访问完为止。
定义Position类用于存储坐标点
起点坐标为(1,1),终点坐标为(8,8)
地图打印在最下面
class Position {
private int px;
private int py;
public Position(int px, int py) {
this.px = px;
this.py = py;
}
public int getPx() {
return px;
}
public void setPx(int px) {
this.px = px;
}
public int getPy() {
return py;
}
public void setPy(int py) {
this.py = py;
}
}
这里我们简单介绍下move()
函数
move函数分别向四个方向移动,然后将可行的path入栈.
注意,这里栈元素中每个栈元素Position都是new出来的,栈中存的是reference,
注意看下面这种写法:
currentPosition.setPy(currentPosition.getPy()+1);
stacks.push(currentPosition);
这种写法一度让我陷入困惑,因为pop出来的Position都是一样的,原因大家可能应该明白了。。。
public void move() {
if (moveRight()) {
Position temp = new Position(currentPosition.getPx() + 1, currentPosition.getPy());
test.add(temp);
stacks.push(temp);
} else if (moveBottom()) {
Position temp = new Position(currentPosition.getPx(), currentPosition.getPy() + 1);
test.add(temp);
stacks.push(temp);
} else if (moveTop()) {
Position temp = new Position(currentPosition.getPx(), currentPosition.getPy() - 1);
test.add(temp);
stacks.push(temp);
} else if (moveLeft()) {
Position temp = new Position(currentPosition.getPx() - 1, currentPosition.getPy());
test.add(temp);
stacks.push(temp);
} else {
currentPosition = stacks.pop();//若当前位置四个方向都走不通,则将当前位置出栈,继续遍历上一节点
}
}
整体代码
class Position {
private int px;
private int py;
public Position(int px, int py) {
this.px = px;
this.py = py;
}
public int getPx() {
return px;
}
public void setPx(int px) {
this.px = px;
}
public int getPy() {
return py;
}
public void setPy(int py) {
this.py = py;
}
}
public class Maze {
private final Position start;//迷宫的起点final
private final Position end;//迷宫的终点final
private ArrayList<String> footPrint;//足迹
private ArrayList<Position> test;
private MyStack<Position> stacks;//自定义栈(也可以用java.util中的Stack栈)若想了解MyStack的实现,可以参考我的另一篇博客
private Position currentPosition;//定义当前位置
public Maze() {//集合,栈的初始化工作
start = new Position(1, 1);
end = new Position(8, 8);
currentPosition = start;
stacks = new MyStack<>();
test = new ArrayList<>();
}
public static final int map[][] = //定义地图10*10的方格
{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 1, 0, 0, 0, 1, 0, 1},
{1, 0, 0, 1, 0, 0, 0, 1, 0, 1},
{1, 0, 0, 0, 0, 1, 1, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 1, 0, 0, 0, 0, 1},
{1, 0, 1, 0, 0, 0, 1, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 1, 1, 0, 1},
{1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
public static void printMap() {//打印地图
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (map[i][j] == 1) System.out.print(" ■");
else System.out.print(" ");
}
System.out.println();
}
}
public boolean moveTop() {//上移
String s = currentPosition.getPx() + "" + (currentPosition.getPy() - 1);
if ((map[currentPosition.getPx()][currentPosition.getPy() - 1] != 1) & !isArrived(s)) {
footPrint.add(s);
return true;
}
return false;
}
public boolean moveRight() {//右移
String s = (currentPosition.getPx() + 1) + "" + currentPosition.getPy();
if (map[currentPosition.getPx() + 1][currentPosition.getPy()] != 1 & !isArrived(s)) {
footPrint.add(s);
return true;
}
return false;
}
public boolean moveBottom() {//下移
String s = currentPosition.getPx() + "" + (currentPosition.getPy() + 1);
if ((map[currentPosition.getPx()][currentPosition.getPy() + 1] != 1) & !isArrived(s)) {
footPrint.add(s);
return true;
}
return false;
}
public boolean moveLeft() {//左移
String s = (currentPosition.getPx() - 1) + "" + currentPosition.getPy();
if ((map[currentPosition.getPx() - 1][currentPosition.getPy()] != 1) & !isArrived(s)) {
footPrint.add(s);
return true;
}
return false;
}
public boolean isArrived(String position) {//判断当前位置是否已经到打过
return footPrint.contains(position);
}
public void move() {//move函数分别向四个方向移动,然后将可行的path入栈
if (moveRight()) {
Position temp = new Position(currentPosition.getPx() + 1, currentPosition.getPy());
test.add(temp);
stacks.push(temp);
} else if (moveBottom()) {
Position temp = new Position(currentPosition.getPx(), currentPosition.getPy() + 1);
test.add(temp);
stacks.push(temp);
} else if (moveTop()) {
Position temp = new Position(currentPosition.getPx(), currentPosition.getPy() - 1);
test.add(temp);
stacks.push(temp);
} else if (moveLeft()) {
Position temp = new Position(currentPosition.getPx() - 1, currentPosition.getPy());
test.add(temp);
stacks.push(temp);
} else {
currentPosition = stacks.pop();//若当前位置四个方向都走不通,则将当前位置出栈,继续遍历上一节点
}
}
public static void main(String[] args) {
Maze m = new Maze();
m.footPrint = new ArrayList<>();
m.footPrint.add("11");
m.stacks.push(m.start);
while (m.currentPosition.getPx() != 8 || m.currentPosition.getPy() != 8) {
m.move();
}
printMap();
System.out.println("下面是足迹,长度是:" + m.footPrint.size());
m.printFootPrint();
}
public void printFootPrint() {
for (int i = 0; i < footPrint.size(); i++) {
System.out.print(footPrint.get(i) + ",");
}
System.out.println();
}
}
大家可能会疑惑,为什么足迹是不连续的(例如:21,12)两个位置是走不通的,是因为在path遍历过程中存在跳栈,既当前位置走不通便会将当前位置的Position出栈(stacks.pop),然后继续上一节点遍历。
更新:
import java.util.HashMap;
import java.util.HashSet;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
public class Maze {
private Position startPosition;
private Position endPosition;
private HashMap<Integer,Integer> pathMap;
public Maze() {
startPosition = new Position(1,1);
endPosition = new Position(8,8);
pathMap = new HashMap<>();
}
private final int mazeZone[][] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,1,0,0,0,0,1,0,1},
{1,0,0,0,1,1,0,0,1,1},
{1,1,1,1,1,0,0,1,0,1},
{1,0,0,0,0,0,0,0,1,1},
{1,0,1,1,1,1,0,0,0,1},
{1,0,0,1,0,1,1,1,1,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
private class Position{
private int indexX;
private int indexY;
public Position(int indexX, int indexY) {
this.indexX = indexX;
this.indexY = indexY;
}
public int getIndexX() {
return indexX;
}
public int getIndexY() {
return indexY;
}
@Override
public boolean equals(Object obj) {
if (obj == null)return false;
Position temp=null;
if (obj instanceof Position) {
temp = (Position) obj;
}else {
return false;
}
if(temp.indexX == this.indexX && temp.indexY == this.indexY)return true;
else return false;
}
@Override
public int hashCode() {
return this.indexX*10+indexY;
}
@Override
public String toString() {
return "["+this.indexX+","+indexY+"]";
}
}
public void printMaze(){
for (int i = 0;i < mazeZone.length;i++){
for (int j = 0;j<mazeZone[0].length;j++){
if(mazeZone[i][j] == 1)System.out.print("\033[46;37;4m"+" "+"\033[0m");
else System.out.print(" ");
}
System.out.println();
}
}
public void startSearch(){
Queue<Position> searchQueue=new LinkedBlockingQueue<>();
HashSet<Position> visitedList=new HashSet<>();
searchQueue.add(startPosition);
visitedList.add(startPosition);
while (!searchQueue.isEmpty()){
Position currentPosition=searchQueue.poll();
int tempX=currentPosition.getIndexX();
int tempY=currentPosition.getIndexY();
if(tempX == endPosition.getIndexX() && tempY == endPosition.getIndexY()){
System.out.println("search finished! path has been detected!");
break;
}
//test move up
if(mazeZone[tempX-1][tempY]==0){
Position p=new Position(tempX-1,tempY);
if (visitedList.add(p)) {
searchQueue.add(p);
System.out.println("["+tempX+","+tempY+"]"+"->"+"["+(tempX-1)+","+tempY+"]");
pathMap.put(new Integer((tempX-1)*10+tempY),new Integer(tempX*10+tempY));
}
}
//test move down
if(mazeZone[tempX+1][tempY]==0){
Position p=new Position(tempX+1,tempY);
if (visitedList.add(p)) {
searchQueue.add(p);
System.out.println("["+tempX+","+tempY+"]"+"->"+"["+(tempX+1)+","+tempY+"]");
pathMap.put(new Integer((tempX+1)*10+tempY),new Integer(tempX*10+tempY));
}
}
//test move left
if(mazeZone[tempX][tempY-1]==0){
Position p=new Position(tempX,tempY-1);
if (visitedList.add(p)) {
searchQueue.add(p);
System.out.println("["+tempX+","+tempY+"]"+"->"+"["+tempX+","+(tempY-1)+"]");
pathMap.put(new Integer(tempX*10+tempY-1),new Integer(tempX*10+tempY));
}
}
//test move right
if(mazeZone[tempX][tempY+1]==0){
Position p=new Position(tempX,tempY+1);
if (visitedList.add(p)) {
searchQueue.add(p);
System.out.println("["+tempX+","+tempY+"]"+"->"+"["+tempX+","+(tempY+1)+"]");
pathMap.put(new Integer(tempX*10+tempY+1),new Integer(tempX*10+tempY));
}
}
}
int temp=pathMap.get(88);
System.out.print(88 + "<-");
while (temp != 11){
System.out.print(temp + "<-");
temp = pathMap.get(temp);
}
System.out.print(11);
System.out.println();
printMaze();
}
}