【问题标题】:How to implement a maze using disjoint sets?如何使用不相交集实现迷宫?
【发布时间】:2015-04-15 20:32:47
【问题描述】:

这是我使用的 DisjointSet 类:

public class DisjointSet{
    public DisjointSet(int size){
        s = new int[size];
        for(int i = 0; i < size; ++i){
            s[i] = -1;
        }
    }

    public void union(int el1, int el2){
        int root1 = find(el1);
        int root2 = find(el2);
        if(root1 == root2){
            return;
        }

        if(s[root2] < s[root1]){
            s[root1] = root2;
        }
        else{
            if(s[root1] == s[root2]){
                --s[root1];
            }
            s[root2] = root1;
        }
    }

    public int find(int x){
        if(s[x] < 0){
            return x;
        }
        else{
            s[x] = find(s[x]);
            return s[x];
        }
    }

    private int[] s;
}

这是我的迷宫课:

public class Maze
{
    public Vector<Wall> maze;
    public Vector<Room> graph;
    public Vector<Integer> path;

    public int LASTROOM;
    public int height;
    public int width;
    public Random generator;
    public DisjointSet ds;

    public int dsSize;

    public Maze(int w, int h, int seed)
    {
        width = w;
        height = h;
        dsSize = width * height;

        LASTROOM = dsSize-1;

        // Maze initialization with all walls
        maze = new Vector<Wall>();
        for(int i = 0; i < height; ++i)
        {
            for(int j = 0; j < width; ++j)
            {
                if(i > 0)
                    maze.add(new Wall(j+i*height, j+(i-1)*height));

                if(j > 0)
                    maze.add(new Wall(j+i*height, j-1+i*height));
            }
        }

        // Creation of the graph topology of the maze
        graph = new Vector<Room>();
        for(int i = 0; i < dsSize; ++i)
            graph.add(new Room(i));

        // Sort the walls of random way
        generator = new Random(seed);

        for(int i = 0; i < maze.size(); ++i)
        {
            //TODO
        }

        // Initialization of related structures
        ds = new DisjointSet(dsSize);
        path = new Vector<Integer>();
    }

    public void generate()
    {
        //TODO
    }

    public void solve()
    {
        //TODO
    }
}

长期以来,我一直在寻找一种方法来实现 generate()solve() 以及迷宫墙壁的随机排序,并且我似乎无法在 Internet 上找到任何算法或实现来执行此操作。

generate() 应该穿过迷宫变量中的置换墙壁,如果墙壁连接的两个部分(房间)不在同一个集合中,则将其摧毁。该方法还应该在房间图中添加一条边(每个房间都有一个名为 paths 的邻接列表,并且 Room 类有一个变量 id标识每个图的顶点)。

solve() 应该求解迷宫路径并生成 Maze 类的向量,其中包含到达出口的房间顺序。第一个房间位于 0,最后一个房间位于 LASTROOM

注意:MazeRoom构造函数如下:

public Wall(int r1, int r2)
{
    room1 = r1;
    room2 = r2;
}

public Room(int i)
{
    id = i;
    distance = -1;
    paths = new Vector<Integer>();
}

如果有人好心推荐一个可以在 Java 中运行的实现,我将不胜感激,谢谢。

【问题讨论】:

    标签: java maze disjoint-sets


    【解决方案1】:

    首先,我真的很喜欢迷宫的想法,并且一直在用 Java 开发一个类似的项目,生成 Torus Mazes

    要生成你的迷宫,你需要看看这个关键的句子:

    ...每个房间都有一个名为路径的邻接列表,房间类有一个变量 id,用于标识每个图的顶点

    这告诉我们什么?它告诉我们我们需要的数据结构!当您处理此类问题时;由边连接的相邻向量,您无疑将创建一个邻接列表。

    有几种不同的方法可以做到这一点,但到目前为止,最简单(并且在这种情况下可以说是最有效的)是创建一个 链表数组。我创建的邻接列表没有使用 Java 库中的内置结构,但如果您选择使用 LinkedList&lt;&gt; 内置结构,则可以使用相同的逻辑。

    /*
     * The Node class creates individual elements that populate the 
     * List class. Contains indexes of the node's neighbors and their
     * respective edge weights
     */
    class Node {
        public int top;
        public int topWeight;
        public int bottom;
        public int bottomWeight;
        public int left;
        public int leftWeight;
        public int right;
        public int rightWeight;
        public int numConnec;
    
        // Default constructor, ititializes neghbors to -1 by default and edge
        // weights to 0
        Node () {
            top    = -1;
            right  = -1;
            bottom = -1;
            left   = -1;
        }
    } // End Node class
    
    
    /*
     * The List class contains Nodes, which are linked to one another
     * to create a Linked List. Used as an adjacency list in the
     * UnionFind class
     */
     class List {
        public Node neighbors;
    
        // Default constructor
        List () {
            neighbors = new Node ();
        }
    
        /**
         * Generates valid edges for the node, also assigns a randomly generated weight to that edge
         * @param i         The row that the node exists on, used to generate outer-node edges
         * @param j         The index of the node in the maze from 0 to (2^p)^2 - 1
         * @param twoP      Represents the dimensions of the maze, used in calculating valid edges
         * @param choice    Randomly generated number to choose which edge to generate
         * @param weight    Randomly generated number to assign generated edge a weight
         * @return          If the assignment was done correctly, returns true. Else returns false.
         */
        public boolean validEdges (int i, int j, int twoP, int choice, int weight) {
            if (neighbors.numConnec < 4) {
                // Top
                if (choice == 0) {
                    neighbors.top = generateTop(i, j, twoP);
                    neighbors.topWeight = weight;
                    neighbors.numConnec++;
                }
    
                // Right
                else if (choice == 1) {
                    neighbors.right = generateRight(i, j, twoP);
                    neighbors.rightWeight = weight;
                    neighbors.numConnec++;
                }
    
                // Bottom
                else if (choice == 2) {
                    neighbors.bottom = generateBottom(i, j, twoP);
                    neighbors.bottomWeight = weight;
                    neighbors.numConnec++;
                }
    
                // Left
                else if (choice == 3) {
                    neighbors.left = generateLeft(i, j, twoP);
                    neighbors.leftWeight = weight;
                    neighbors.numConnec++;
                }
            }
            else {
                return false;
            }
            return true;
        }
    
        public int generateTop (int i, int j, int twoP) {
            int neighbor = 0;
    
            // Set the top neighbor
            if (i == 0) {
                neighbor = j + twoP * (twoP + (-1));
            }
            else {
                neighbor = j + (-twoP);
            }
            return neighbor;
        }
    
        public int generateRight (int i, int j, int twoP) {
            int neighbor = 0;
    
            // Set the right neighbor
            if (j == twoP * (i + 1) + (-1)) {
                neighbor = twoP * i;
            }
            else {
                neighbor = j + 1;
            }
            return neighbor;
        }
    
        public int generateBottom (int i, int j, int twoP) {
            int neighbor = 0;
    
            // Set the bottom neighbor
            if (i == twoP + (-1)) {
                neighbor = j - twoP * (twoP + (-1));
            }
            else {
                neighbor = j + twoP;
            }
            return neighbor;
        }
    
        public int generateLeft (int i, int j, int twoP) {
            int neighbor = 0;
    
            // Set the left neighbor
            if (j == twoP * i) {
                neighbor = twoP * (i + 1) + (-1);
            }
            else {
                neighbor = j + (-1);
            }
            return neighbor;
        }
    } // End List class
    

    为了解决迷宫,这听起来像是 Dijkstra 算法的实现可以解决的问题。

    Dijkstra 的工作是从您的第一个节点开始创建已知集。然后,您确定到下一条边的最短路径并将该节点添加到已知集合中。每次寻找下一条最短路径时,都会添加从第一个节点经过的距离。

    这个过程一直持续到所有节点都在已知集合中并且计算出到达目标的最短路径。

    Shortest Paths

    【讨论】:

    • 邻接列表包含在Vector&lt;Integer&gt; paths中。我可以用paths 做类似于您在代码中所做的事情吗?
    • 绝对!使用的结构可能会有所不同,但大部分逻辑将保持不变。您只需创建一个向量数组而不是链表。 @Zachariel
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    • 2012-08-02
    • 2020-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多