Java用递归解决迷宫问题

发布于 2024-09-08 21:04:15 字数 4126 浏览 4 评论 0原文

我有一项任务,我应该能够显示从入口到出口的迷宫路径,并且我已经让它在一定程度上发挥作用,但是当迷宫变得更加复杂并出现死胡同时,程序就会进入无限递归。如果您能给我任何帮助,为我指明正确的方向,我将不胜感激。

Mu电流理论可以在Room类中找到。

这里是 Room 类,其中存储了连接迷宫的每个房间的引用,有点像北、南、东、西、上、下 6 个方向链接的链表。

import java.util.ArrayList;

public class OurRoom
{
    private OurRoom exits[];
    private String name;
    private static ArrayList<OurRoom> list;

    public OurRoom()
    {
        this(null);
    }

    public OurRoom(String name)
    {
        this.name = name;
        this.list = new ArrayList<OurRoom>();
        exits = new OurRoom[Direction.values().length];
        for(OurRoom exit : exits)
        {
            exit = null;
        }
    }       


    public void connectTo(OurRoom theOtherRoom, Direction direction)
    {
        exits[direction.ordinal()] = theOtherRoom;
        theOtherRoom.exits[direction.getOpposite().ordinal()] = this;
    }

    public OurRoom getExit(Direction direction)
    {
        return exits[direction.ordinal()];
    }

    public boolean lookExit(Direction direction)
    {
        return exits[direction.ordinal()] != null;
    }

    public String getName() {
        return name;
    }

    public OurRoom solveRecursively(OurRoom exit) {
        list.add(this);
        if(this == exit) {
            return this;
        }else { 
            OurRoom temp = null;            
            if(lookExit(Direction.east)) {
                temp = exits[Direction.east.ordinal()].solveRecursively(exit);              
            }   
            else if(lookExit(Direction.up)) {
                temp = exits[Direction.up.ordinal()].solveRecursively(exit);
            }
            else if(lookExit(Direction.south)) {
                temp = exits[Direction.south.ordinal()].solveRecursively(exit);             
            }           
            else if(lookExit(Direction.down)) {
                temp = exits[Direction.down.ordinal()].solveRecursively(exit);
            }
            else if(lookExit(Direction.west)) {
                temp = exits[Direction.west.ordinal()].solveRecursively(exit);      
            }   
            else if(lookExit(Direction.north)) {
                temp = exits[Direction.north.ordinal()].solveRecursively(exit); 
            }
            return temp;
        }
    }

    public ArrayList<OurRoom> getList() {
        return list;
    }

}

这是 Direction 枚举

public enum Direction
{
    north, south, east, west, up, down;

    public Direction getOpposite()
    {
        switch(this)
        {
            case north:
                return south;
            case south:
                return north;
            case east:
                return west;
            case west:
                return east;
            case up:
                return down;
            case down:
                return up;
            default:
                return this;
        }
    }
}

,这是迷宫如何构建的示例。

import java.util.ArrayList;
import java.util.Iterator;

public class OurMaze
{
    private OurRoom entrance, exit;

    public OurMaze()
    {
        this(1);
    }

    public OurMaze(int mazeNumber)
    {
        entrance = null;
        exit = null;
        switch(mazeNumber)
        {
        case 0:
            break;
        case 1:
            this.buildMaze1();
            break;             
        default:
        }
    }

    public OurRoom getEntrance()
    {
        return entrance;
    }

    public OurRoom getExit()
    {
        return exit;
    }

    public Iterator<OurRoom> findPathRecursively() {
        entrance.solveRecursively(exit);
        ArrayList<OurRoom> list = entrance.getList();       
        return list.iterator();
    }

    private void buildMaze1()
    {
        OurRoom room1, room2;

        room1 = new OurRoom("Room 1");
        room2 = new OurRoom("Room 2");
        room1.connectTo(room2, Direction.north);

        entrance = room1;
        exit = room2;
    }

    public static void main(String[] args) {
        OurMaze maze = new OurMaze(1);      
    }
}

I have an assignment where I am supposed to be able to display the path of a maze from the entrance to the exit and I have gotten it to work to a degree but when the maze gets more complicated with dead ends and such the program goes into infinite recursion. If you could give me any help to point me in the right direction it would be much appreciated.

Mu current theory can be found in the Room class.

Here is the Room class where the references to each room connecting the maze are stored, kind of like a linked list linked in 6 directions, north, south, east, west, up, and down.

import java.util.ArrayList;

public class OurRoom
{
    private OurRoom exits[];
    private String name;
    private static ArrayList<OurRoom> list;

    public OurRoom()
    {
        this(null);
    }

    public OurRoom(String name)
    {
        this.name = name;
        this.list = new ArrayList<OurRoom>();
        exits = new OurRoom[Direction.values().length];
        for(OurRoom exit : exits)
        {
            exit = null;
        }
    }       


    public void connectTo(OurRoom theOtherRoom, Direction direction)
    {
        exits[direction.ordinal()] = theOtherRoom;
        theOtherRoom.exits[direction.getOpposite().ordinal()] = this;
    }

    public OurRoom getExit(Direction direction)
    {
        return exits[direction.ordinal()];
    }

    public boolean lookExit(Direction direction)
    {
        return exits[direction.ordinal()] != null;
    }

    public String getName() {
        return name;
    }

    public OurRoom solveRecursively(OurRoom exit) {
        list.add(this);
        if(this == exit) {
            return this;
        }else { 
            OurRoom temp = null;            
            if(lookExit(Direction.east)) {
                temp = exits[Direction.east.ordinal()].solveRecursively(exit);              
            }   
            else if(lookExit(Direction.up)) {
                temp = exits[Direction.up.ordinal()].solveRecursively(exit);
            }
            else if(lookExit(Direction.south)) {
                temp = exits[Direction.south.ordinal()].solveRecursively(exit);             
            }           
            else if(lookExit(Direction.down)) {
                temp = exits[Direction.down.ordinal()].solveRecursively(exit);
            }
            else if(lookExit(Direction.west)) {
                temp = exits[Direction.west.ordinal()].solveRecursively(exit);      
            }   
            else if(lookExit(Direction.north)) {
                temp = exits[Direction.north.ordinal()].solveRecursively(exit); 
            }
            return temp;
        }
    }

    public ArrayList<OurRoom> getList() {
        return list;
    }

}

Here is the Direction enum

public enum Direction
{
    north, south, east, west, up, down;

    public Direction getOpposite()
    {
        switch(this)
        {
            case north:
                return south;
            case south:
                return north;
            case east:
                return west;
            case west:
                return east;
            case up:
                return down;
            case down:
                return up;
            default:
                return this;
        }
    }
}

And here is an example of how the maze is built.

import java.util.ArrayList;
import java.util.Iterator;

public class OurMaze
{
    private OurRoom entrance, exit;

    public OurMaze()
    {
        this(1);
    }

    public OurMaze(int mazeNumber)
    {
        entrance = null;
        exit = null;
        switch(mazeNumber)
        {
        case 0:
            break;
        case 1:
            this.buildMaze1();
            break;             
        default:
        }
    }

    public OurRoom getEntrance()
    {
        return entrance;
    }

    public OurRoom getExit()
    {
        return exit;
    }

    public Iterator<OurRoom> findPathRecursively() {
        entrance.solveRecursively(exit);
        ArrayList<OurRoom> list = entrance.getList();       
        return list.iterator();
    }

    private void buildMaze1()
    {
        OurRoom room1, room2;

        room1 = new OurRoom("Room 1");
        room2 = new OurRoom("Room 2");
        room1.connectTo(room2, Direction.north);

        entrance = room1;
        exit = room2;
    }

    public static void main(String[] args) {
        OurMaze maze = new OurMaze(1);      
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

—━☆沉默づ 2024-09-15 21:04:15

您只需要保留二维数组,其中的值指示单元格是否被访问:您不想两次访问同一个单元格。

除此之外,它只是广度优先搜索(深度优先搜索也可以,如果你不需要最短路径)。

一些链接
http://en.wikipedia.org/wiki/Flood_fill
http://en.wikipedia.org/wiki/Breadth-first_search
http://en.wikipedia.org/wiki/Depth-first_search

示例搜索例程。

    void dfs(int i, int j) {
        if cell(i, j) is outside of maze or blocked {
            return
        }
        if visited[i][j] {
            return
        }
        visited[i][j] = true;
        dfs(i + 1, j);
        dfs(i - 1, j);
        dfs(i, j + 1);
        dfs(i, j - 1);
    }

路径本身可以被找到,就像visited一样,对于每个单元格,你保留了你到达它的单元格。因此,打印看起来像这样(只是伪代码)。

var end = exit_point;
while (end != start_point) {
   print end;
   end = came_from[end];
}

编辑
上面的代码适用于二维迷宫,我刚刚注意到你有三维版本。但在上面的例子中引入第三个坐标很容易。
如果有任何困难请告诉我。

You just need to keep two-dimensional array with values indicating whether the cell was visited or not: you don't want to go through the same cell twice.

Apart from that, it's just breadth-first-search (depth-first-search is fine too, if you don't want shortest path).

Some links
http://en.wikipedia.org/wiki/Flood_fill
http://en.wikipedia.org/wiki/Breadth-first_search
http://en.wikipedia.org/wiki/Depth-first_search

Sample search routine.

    void dfs(int i, int j) {
        if cell(i, j) is outside of maze or blocked {
            return
        }
        if visited[i][j] {
            return
        }
        visited[i][j] = true;
        dfs(i + 1, j);
        dfs(i - 1, j);
        dfs(i, j + 1);
        dfs(i, j - 1);
    }

Path itself can be found if, like with visited, for each cell you keep cell from which you came to it. So, printing would look like this (just a pseudocode).

var end = exit_point;
while (end != start_point) {
   print end;
   end = came_from[end];
}

edit
The code above is for two-dimensional maze and I just noticed that you have three-dimensional version. But it's easy to introduce third coordinate in the example above.
Let me know if there're any difficulties.

离去的眼神 2024-09-15 21:04:15

其他人已经描述了解决此问题的适当方法,但我认为值得准确指出为什么您的程序无法扩展到更复杂的迷宫。

正如达菲莫所暗示的那样,问题在于你的算法没有正确执行任何回溯——当它采用一个结果是死胡同的分支并返回到前一个方块时,它根本不记得这一点。由于它以固定顺序尝试退出,因此它总是立即重试失败的退出。

看看 solveRecursively 函数是如何定义的,您会发现在任何给定的房间中,只会尝试一个方向。如果一个房间有一个向东的出口,那么它是否有任何其他出口也并不重要,因为 if-else 块将永远考虑它们。

因此,事实证明,在正确的解决方案不是按顺序从每个房间的“第一个”出口的任何情况下,您的求解逻辑都会失败(即进入两个房间之间的无限循环)你已经在那里定义了。

(一个快速解决方法是针对每个房间/方向存储一个简单的布尔标志。在调用递归调用之前设置它,然后如果您最终再次回到那个房间,您知道那个方向不会无法解决,可以让 if 块失败以尝试其他退出之一,如 Nikita 所建议的那样,重构它以使用典型的 BFS 逻辑,总体上会更好)

Others have described appropriate approaches to solving this problem, but I think it's worth pointing out exactly why your program won't scale to more complex mazes.

As duffymo hinted, the problem is that your algorithm doesn't do any backtracking correctly - when it takes a branch that turns out to be a dead end, and returns to a previous square, it doesn't remember this at all. And since it tries the exits in a fixed order, it will always retry that failed exit immediately.

Look at how the solveRecursively function is defined, and you'll see that from any given room, only one direction would ever be tried. If a room has an exit to the east, then it doesn't even matter if it has any other exits since the if-else block would never consider them.

So as it turns out, your solving logic will fail (i.e go into an infinite loop between two rooms) in any case where the correct solution isn't the "first" exit from each room in the order you've defined there.

(A quick fix would be to store a simple boolean flag against each room/direction. Set it before you call the recursive call, then if you end up back in that room again, you know that direction doesn't work out and can let the if block fall through to try one of the other exits. Refactoring this to use typical BFS logic, as Nikita suggests, would be better overall)

国粹 2024-09-15 21:04:15

我敢打赌你需要一棵树来记录你去过的地方。

当递归失败时,通常意味着编写该方法的人没有正确表达停止条件。你的是什么?

我想这是我在电脑上玩的第一个游戏。这是我获得本科学位的学校的一台 IBM 大型机。 I/O 是在纸质电传打字机上进行的。许多人因玩这个迷宫游戏而被冲走的账户资金流下了眼泪。很有趣。

I'd bet you need a tree of some kind to keep track of where you've been.

When recursion fails, it usually means that the person writing the method didn't expression the stopping condition properly. What's yours?

I think this was the first game I ever encountered on a computer. It was an IBM mainframe at the school where I got my undergraduate degree. The I/O was on a paper teletype. Many salt tears were wept at the account dollars that were flushed away playing this maze game. Great fun.

趴在窗边数星星i 2024-09-15 21:04:15

解决迷宫时,将其表示为 6 元图,其中每个节点是一个房间,每条边代表六个方向之一的行进。然后,您可以应用一些众所周知的算法来查找最短路径。

此页面描述了通过此类结构的图形查找路径的各种解决方案。您的图表比描述现实世界地图的图表更容易,因为沿着任何边行驶的成本等于沿着任何其他边行驶的成本。

请务必查看 Dijkstra 算法

When solving the maze, represent it as a 6-ary graph where each node is a room and each edge represents travel in one of the six directions. You can then apply some of the well known algorithms for finding shortest paths.

This page describes various solutions for finding paths through graphs that are structured as such. Your graph is easier than the ones that describe real-world maps, since the cost of traveling down any edge is equal to the cost of traveling down any other edge.

Be especially sure to look at Dijkstra's algorithm.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文