如何在java中使用2d数组迷宫查找路径

如何在java中使用2d数组迷宫查找路径,java,recursion,flood-fill,Java,Recursion,Flood Fill,这里, S=起点(2,2) B=块 O=打开 X=退出 我想做一个迷宫,检查北、西、东、南。如果X在附近,它将返回程序。如果没有,则检查起点周围是否有“O”,并递归传递新的起点。如果无路可走,且未找到“X”,则将返回原始起点(2,2),并检查西部、东部和南部 节目结束后,我得到: B B B B B B B B O B B B S O B B O O B B B B X B B 但是,我希望递归后的输出是: B B B B B B B B + B B B + + B B O +

这里,

S=起点(2,2)

B=块

O=打开

X=退出

我想做一个迷宫,检查北、西、东、南。如果X在附近,它将返回程序。如果没有,则检查起点周围是否有“O”,并递归传递新的起点。如果无路可走,且未找到“X”,则将返回原始起点(2,2),并检查西部、东部和南部

节目结束后,我得到:

B B B B B

B B B O B

B B S O B

B O O B B

B B X B B
但是,我希望递归后的输出是:

B B B B B

B B B + B

B B + + B

B O + B B

B B X B B
这是我现在的代码:

B B B B B

B B B - B

B B + - B

B O + B B

B B X B B
使用全局变量(标志)确定是否找到了正确的路径

例如:

BBBBB
BBOOB
XOBOB
BSOBB
BBBBB

BBBBB
BBOOB
X+BOB ( It should stop right here, because there is X around it.)
BSOBB
BBBBB

BBBBB
BBOOB
X+BOB (but, somehow it go to the right of the startpoint and mark the 'O' to  '+'
BS+BB
BBBBB
公共类您的类{
静态布尔值found=false;//全局标志
//您现有的代码
公共静态void Find_Path(char[][]迷宫、int startX、int startY){
// ....
对于(int i=0;i<4;i++){
// ...
如果(迷宫[后X][后y]=='X'){
found=true;//找到路径
返回;
}
}
对于(int i=0;i<4;i++){
// ...
if(find)//在前面的递归调用中已找到路径;无需再搜索
返回;
否则{//尚未找到路径,必须继续搜索
如果(迷宫[后X][后Y]=='O'){
找到路径(迷宫、后x、后y);
如果(!found){//未找到路径
迷宫[afterX][afterY]='-';
}
}
}
}
}
}

您正在寻找的算法称为广度优先搜索和深度优先搜索。你会遇到的一个问题是,你的迷宫中是否存在一个循环。例如,如果你有这个会发生什么

public class YourClass{
    static boolean found = false; // the global flag
    // your existing code

    public static void Find_Path(char[][] maze, int startX, int startY){
        // ....
        for(int i = 0; i < 4 ; i++){
            // ...
            if(maze[afterX][afterY] == 'X'){
                found = true; // path found
                return;
            }
        }
        for(int i = 0; i < 4 ; i++){
            // ...
            if(found) // path already found in earlier recursive call; no need to search anymore
                return;
            else{ // path not found yet, have to continue searching
                if(maze[afterX][afterY] == 'O'){
                    Find_Path(maze, afterX, afterY);
                    if(!found){ // path not found
                        maze[afterX][afterY] = '-';
                    }
                }
            }
        }
    }
}
然后,算法可能会陷入一个无法逃脱的循环中

解决此问题的经典方法是使用另一个数据结构“着色”顶点,该数据结构表示以前是否访问过顶点

本次麻省理工开放式课程可能会为您指明正确的方向:


不过,要直接回答您的问题,请考虑基本情况。当你找到X时,是什么阻止了循环的旋转?在当前状态下,停止迭代/递归的唯一原因似乎是您没有地方可以查看。您需要某种类型的变量来告诉第二个for循环停止搜索。

当问题被提出时,此解决方案应该有效:

B B B B B
B O O O B
B O S O B
B O O O B
B B B B B
例如@William John Howard设计的迷宫没有解决方案:

Solved, A correct path is: S
[B, B, B, B, B]
[B, B, B, -, B]
[B, B, +, -, B]
[B, O, +, B, B]
[B, B, X, B, B]
输出为:

import java.util.Arrays;

public class TwoDSolver {

private char[][] maze;
private String currPath;
private int currX;
private int currY;
private boolean unsolvable;

public static void main(String[] args) {
    char[][] customMaze = {
            {'B', 'B', 'B', 'B', 'B'},
            {'B', 'B', 'B', 'O', 'B'},
            {'B', 'B', 'S', 'O', 'B'},
            {'B', 'O', 'O', 'B', 'B'},
            {'B', 'B', 'X', 'B', 'B'}
    };

    String startPath = "";
    int startX = 2;
    int startY = 2;
    TwoDSolver solver = new TwoDSolver(customMaze, startX, startY, startPath, false);
    // place a plus at the start point
    solver.placePlus();
    solver.solveMaze();
    if (solver.unsolvable) {
        System.out.println("The maze is unsolvable");
    } else {
        System.out.println("Solved, A correct path is: " + solver.currPath);
    }
    solver.printMaze();


}

// constructor
TwoDSolver(char[][]aMaze, int stX, int stY, String currentPath, boolean noSolution) {
    maze = aMaze;
    currX = stX;
    currY = stY;
    currPath = currentPath;
    unsolvable = noSolution;
}

// indicate taken path
void placePlus() {
    maze[currX][currY] = '+';
}

// for backtracking
void placeMinus() {
    maze[currX][currY] = '-';
}

// solve
// priority in this order East, West, South, North
void solveMaze() {
    // check for a win
    if (checkForWin()) {
        return;
    }
    // No win, so let's check for an opening
    // check east
    if (currY + 1 < maze[currX].length && checkForOpen(currX, currY + 1)) {
        currY++;
        placePlus();
        currPath += "E"; // Append East to our current path
        // recursive call continue searching
        solveMaze();
        // check west
    } else if (currY - 1 >= 0 && checkForOpen(currX, currY - 1)) {
        currY--;
        placePlus();
        currPath += "W";
        solveMaze();
        // check south
    }  else if (currX + 1 < maze.length && checkForOpen(currX + 1, currY)) {
        currX++;
        placePlus();
        currPath += "S";
        solveMaze();
        // check north
    }  else if (currX - 1 >= 0 && checkForOpen(currX - 1, currY)) {
        currX--;
        placePlus();
        currPath += "N";
        solveMaze();
    } else { // we've hit a dead end, we need to backtrack
        if (currPath.length() == 0) {
            // we're back at the starting point, the maze is unsolvable
            unsolvable = true;
            return;
        } else {
            // we've reached a dead end, lets backtrack
            placeMinus();
            backTrack();
        }
    }
}

// see if the spot at a give x, y is open
boolean checkForOpen(int x, int y) {
    return maze[x][y] == 'O';
}

// see if any of the surrounding spots are the exit
boolean checkForWin() {
    // make sure to protect against out of bounds as well
    return ((currY + 1 < maze[currX].length && maze[currX][currY + 1] == 'X') ||
            (currY - 1 >= 0  && maze[currX][currY - 1] == 'X') ||
            (currX + 1 < maze[currX].length && maze[currX + 1][currY] == 'X') ||
            (currX -1 >= 0 && maze[currX -1][currY] == 'X'));
}

void backTrack() {
    // sanity chek currPath.length() should always be > 0 when we call backTrack
    if (currPath.length() > 0) {
        placeMinus();
        switch (currPath.charAt(currPath.length() - 1)) {
        case 'E':
            currY--;
            break;
        case 'W':
            currY++;
            break;
        case 'S':
            currX--;
            break;
        case 'N':
            currX++;
            break;
        }
        currPath = currPath.substring(0, currPath.length()-1);
        solveMaze();    
    }
}

void printMaze() {
    for (int i = 0; i < maze.length; i++) {
        System.out.println(Arrays.toString(maze[i]));
    }
}

}
{'B', 'B', 'B', 'B', 'B'},
{'B', 'O', 'O', 'O', 'B'},
{'B', 'O', 'S', 'O', 'B'},
{'B', 'O', 'O', 'O', 'B'},
{'B', 'B', 'B', 'B', 'B'}
关于此解决方案和解决问题的方法,需要注意一点: 这不会提供到出口的最短路径

这个解决方案的优先顺序是:东、西、南、北

这里有一个例子来说明我的意思:

启动迷宫:

The maze is unsolvable
[B, B, B, B, B]
[B, -, -, -, B]
[B, -, +, -, B]
[B, -, -, -, B]
[B, B, B, B, B]
输出:

{'B', 'B', 'B', 'X', 'B'},
{'B', 'O', 'O', 'O', 'B'},
{'B', 'O', 'S', 'O', 'B'},
{'B', 'O', 'O', 'O', 'B'},
{'B', 'B', 'B', 'B', 'B'}
如您所见,出口有多条正确路径,NE、EN、WNE、SENN、SWNNEE、ESWWNNEE(此算法选择的路径是因为方向优先级)


我认为您发布的代码中缺少的主要内容是跟踪当前路径的方法,以及在遇到死胡同时进行回溯的方法。

在问题中,OP试图使用它来解决迷宫。谢谢,但退出点前面的“O”int将变为“-”,pl,请检查一下这个问题,了解我在说什么,把所有的“O”都变成“-”不是所有的
O
;只有属于错误路径的
O
-s。当找到
X
时,
found
将变为
true
-这将停止任何进一步的
O
->
-
转换。我在代码中犯了一个错误-我没有将
found
声明为静态。现在看看这是否如您预期的那样有效。对于某些情况,它会将所有的“O”变成“+”,您能解释一下原因吗?像这个例子一样,我提出了我的问题。
{'B', 'B', 'B', 'X', 'B'},
{'B', 'O', 'O', 'O', 'B'},
{'B', 'O', 'S', 'O', 'B'},
{'B', 'O', 'O', 'O', 'B'},
{'B', 'B', 'B', 'B', 'B'}
Solved, A correct path is: ESWWNNEE
[B, B, B, X, B]
[B, +, +, +, B]
[B, +, +, +, B]
[B, +, +, +, B]
[B, B, B, B, B]