使用DFS进行迷宫回溯

时间:2015-02-02 17:54:41

标签: java algorithm search graph-theory maze

我无法使用DFS作为起点确定迷宫中的回溯算法,之后我想要包含启发式成本来确定最短路径。给我一个0和1的迷宫,我的猎人节点必须找到猎物节点。我已经完成了我所能做的事情但是当它到达死胡同时它将会跳过#34;到我的堆栈中的下一个可步行节点,这不是我想要的,我需要能够回溯到那个可步行的节点,并从那里继续探索迷宫。

1是Path,0是wall。

0001000001
0111110101
0100010101
0111011101
0001010001
0111010101
0100010101
0111011101
0001000001
1111111111

主要课程:

 public class main {

        public static void main(String[] args) {

            //create maze from file
            MazeGenerator mazeGenerationTest = new MazeGenerator();
            mazeGenerationTest.generateFromTextFile();

            //create hunter and prey nodes
            Node hunter = new Node(0,3);
            Node prey = new Node(0,9);
            //setup maze
            MazeTraversal mazeTraverse = new MazeTraversal(mazeGenerationTest.getNodeGrid(),hunter,prey);
            mazeTraverse.setCols(mazeGenerationTest.getCols());
            mazeTraverse.setRows(mazeGenerationTest.getRows());
            //run algo
            mazeTraverse.dfs();
            mazeGenerationTest.seeMaze();

        }

    }

用于从文本文件中读取的Maze Generator类

   public class MazeGenerator {

        private Node nodeGrid[][];
        private ArrayList<Node> adjList = new ArrayList<Node>();
        private int rows = 0;
        private int cols = 0;
        public MazeGenerator(){

        }

        public void generateFromTextFile(){
            BufferedReader br = null;
            try {
                br = new BufferedReader(new FileReader(CONSTANTS.MAZE_PATH));
                StringBuilder sb = new StringBuilder();
                String line = br.readLine();
                cols = line.length();
                while (line != null) {
                    rows++;

                    sb.append(line);
                    sb.append(System.lineSeparator());
                    line = br.readLine();
                }

                br.close();
                //set 2d array of node according to rows and cols read from text file, and init nodes
                setNodeGrid(getRows(),getCols(),sb.toString());
                //seeMaze();
                //manualAdjList();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e){
                e.printStackTrace();
            }

        }

        public Node[][] getNodeGrid() {
            return nodeGrid;
        }

        /***
         * Creates and initializes a 2d node grid.
         * @param rows The number of rows for initializing the 2d array
         * @param cols The number of columns for initializing the 2d array
         * @param mazeTxt The string of maze text read from the file
         */
        public void setNodeGrid(int rows, int cols,String mazeTxt) {
            this.nodeGrid = new Node[rows][cols];
            int nodesAccessed = 0;
            //initialise nodes into grid 
            for(int r = 0; r < rows; r++){
                for(int c = 0; c < cols; c++){
                    if(mazeTxt.charAt(nodesAccessed) != '\n' && mazeTxt.charAt(nodesAccessed) != '\r'){
                        if(mazeTxt.charAt(nodesAccessed) == '1'){//if node is 1 == path
                            nodeGrid[r][c] = new Node(r,c,false);
                        }else{
                            nodeGrid[r][c] = new Node(r,c,true);//node == 0 is wall
                        }
                    }
                    else{
                        --c;
                    }

                    nodesAccessed++;
                }
            }

        }

        public int getRows() {
            return rows;
        }


        public int getCols() {
            return cols;
        }

        public void seeMaze(){
            for(int r = 0; r < getRows();r++){
                for(int c = 0; c< getCols();c++){
                    getNodeGrid()[r][c].toString();

                }
            }
        }

    }

遍历算法的迷宫遍历类

    public MazeTraversal(Node[][] nodeGrid,Node hunter,Node prey){
        this.nodeGrid = nodeGrid;
        this.hunter = hunter;
        this.prey = prey;

    }
    public void dfs(){

        Stack<Node> stack = new Stack<Node>();
        Node hunterNode = null;
        Node preyNode = null;
        Node currentNode = null;

        hunterNode = nodeGrid[getHunter().getX()][getHunter().getY()];
        preyNode = nodeGrid[getPrey().getX()][getPrey().getY()];
        //Add hunter node to stack
        stack.push(hunterNode);
        System.out.println("Pushed Start-"+hunterNode.toString());
            while(!stack.isEmpty()){//while there are items in the stack

                currentNode = stack.pop();//pop  into currentNode
                System.out.println("Popped-"+currentNode.toString());
                if(currentNode.equals(preyNode)){
                    System.out.println("Found!");
                    return;
                }
                if(!currentNode.isDiscovered()){//if the current node is not yet discovered
                    currentNode.setDiscovered(true);//we mark it as discovered
                    generateNeighboursForNode(currentNode);//discover all neighbours of currentNode and add it to the nodes neighbour list

                    for(Node neighbour : currentNode.getNeighboursList()){//for all neighbour of current node
                        if(neighbour.getPredecessor()==null)//we do this check so the next node cannot replace the parent node
                            neighbour.setPredecessor(currentNode);//and add currentNode as their parentNode.
                        if(!neighbour.equals(currentNode.getPredecessor()))//if this neighbour is not equal to the parent
                        {
                            stack.push(neighbour);// we push to stack, so we do not add the parent into the stack again
                            System.out.println("Pushed-"+neighbour.toString());
                        }
                    }
                }else{//discovered
                    System.out.println("This node is discovered ! " +currentNode.toString());
                    boolean allDiscovered = true;//assume all is discovered
                    for(Node neighbour : currentNode.getNeighboursList()){
                        if(!neighbour.isDiscovered()){//for each neighbour that is not yet discovered;push neighbour
                            stack.push(neighbour);
                            System.out.println("Pushed-"+neighbour.toString());
                            allDiscovered = false;//as long as one neighbour is not yet discovered
                        }
                    }
                    if(allDiscovered){//if all neigbour is discovered
                        stack.push(currentNode.getPredecessor());//backtrack to parent;push parent 
                        System.out.println("Pushed-"+currentNode.toString());
                    }

                }

            }//while end
    }

    public Node getHunter() {
        return hunter;
    }

    public void setHunter(Node hunter) {
        this.hunter = hunter;
    }

    public Node getPrey() {
        return prey;
    }

    public void setPrey(Node prey) {
        this.prey = prey;
    }
    public int getRows() {
        return rows;
    }

    public void setRows(int rows) {
        this.rows = rows;
    }

    public int getCols() {
        return cols;
    }

    public void setCols(int cols) {
        this.cols = cols;
    }

    public MazeTraversal(Node[][] nodeGrid){
        this.nodeGrid = nodeGrid;

    }

    public void generateNeighboursForNode(Node node){
        //add adj nodes
        int nodeX = node.getX();
        int nodeY = node.getY();

        Node currentNode = nodeGrid[nodeX][nodeY];

        if(currentNode.isWall()){//if we are looking at a wall we return
            return;
        }
        Node rightNode = null;
        if (nodeY < cols - 1){ // the condition for the existence of a right node
            rightNode = nodeGrid[nodeX][nodeY+1];
            if(!rightNode.isWall()){
                currentNode.addNeighbor(rightNode);
            }
        }

        Node bottomNode = null;
        if (nodeX < rows - 1){
            bottomNode = nodeGrid[nodeX+1][nodeY];// the condition for the existence of a bottom node
            if(!bottomNode.isWall()){
                currentNode.addNeighbor(bottomNode);
            }
        }

        Node leftNode = null;
        if (nodeX > 0){
            leftNode = nodeGrid[nodeX-1][nodeY];// the condition for the existence of a left node
            if(!leftNode.isWall()){
                currentNode.addNeighbor(leftNode);
            }
        }

        Node topNode = null;
        if (nodeY > 0){
            topNode = nodeGrid[nodeX][nodeY-1];// the condition for the existence of a top node
            if(!topNode.isWall()){
                currentNode.addNeighbor(topNode);
            }
        }
        System.out.println("Generating neighbours "+node.toString());
    }
}

节点类

public class Node {

    private Node predecessor;//previous node
    private boolean isWall;
    private boolean isDiscovered;
    private int x;
    private int y;
    private ArrayList<Node> neighbours = new ArrayList<Node>();
    //for a star
    private int hCost;
    public Node(int x , int y){
        this.x = x;
        this.y = y;
        this.predecessor = null;
        this.isWall = false;
        this.isDiscovered = false;
    }
    public Node(int x , int y,boolean isWall){
        this.x = x;
        this.y = y;
        this.predecessor = null;
        this.isWall = isWall;
        this.isDiscovered = false;
    }
    //add a neighbor to this cell, and this cell as a neighbor to the other
    public void addNeighbor(Node other) {
        if (!this.neighbours.contains(other)) { // avoid duplicates
            this.neighbours.add(other);


        }
    }
    public Node getPredecessor() {
        return predecessor;
    }
    public void setPredecessor(Node predecessor) {
        this.predecessor = predecessor;
    }
    public boolean isWall() {
        return isWall;
    }
    public void setWall(boolean isWall) {
        this.isWall = isWall;
    }
    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
    public int getY() {
        return y;
    }
    public void setY(int y) {
        this.y = y;
    }

    public int gethCost() {
        return hCost;
    }
    public void sethCost(int hCost) {
        this.hCost = hCost;
    }


    public boolean isDiscovered() {
        return isDiscovered;
    }
    public void setDiscovered(boolean isDiscovered) {
        this.isDiscovered = isDiscovered;
    }
    public ArrayList<Node> getNeighboursList() {
        return neighbours;
    }

    public String coordStr(){
        return "("+this.x+","+this.y+")";
    }


    @Override
    public boolean equals(Object other) {
        if (!(other instanceof Node)) return false;
        Node otherCell = (Node) other;
        return (this.x == otherCell.x && this.y == otherCell.y);
    }


    @Override
    public int hashCode() {
        // random hash code method designed to be usually unique
        return this.x + this.y * 256;
    }
    @Override
    public String toString() {
        String coords ="";
        String parent = "";
        String neighbour ="";
        String discovered ="";

        coords = "Node:"+coordStr();
        if(getPredecessor() !=null)
            parent = " Parent Node:"+getPredecessor().coordStr();

        neighbour = " Neighbours:";
        for(Node n: getNeighboursList()){
            neighbour+= n.coordStr()+n.isDiscovered;
        }
        discovered = " isDiscovered():"+isDiscovered();
        return coords+discovered+parent+neighbour;
    } 

}

为了解释跳转的含义,从我的调试信息中,你可以看到从9,0开始它会跳到9,4但不应该是这样,而应该回到9,3并从那里继续到9,4

Pushed-Node:(9,3)isDiscovered():false父节点:(8,3)邻居:

Popped-Node:(9,3)isDiscovered():false父节点:(8,3)邻居:

生成邻居节点:(9,3)isDiscovered():true父节点:(8,3) 邻居:(9,4)假的(8,3)真(9,2)假

Pushed-Node:(9,4)isDiscovered():false父节点:(9,3)邻居:

Pushed-Node:(9,2)isDiscovered():false父节点:(9,3)邻居:

Popped-Node:(9,2)isDiscovered():false父节点:(9,3)邻居:

生成邻居节点:(9,2)isDiscovered():true父节点:(9,3)邻居:(9,3)true(9,1)false

Pushed-Node:(9,1)isDiscovered():false父节点:(9,2)邻居:

Popped-Node:(9,1)isDiscovered():false父节点:(9,2)邻居:

生成邻居节点:(9,1)isDiscovered():true父节点:(9,2)邻居:(9,2)true(9,0)false

Pushed-Node:(9,0)isDiscovered():false父节点:(9,1)邻居:

Popped-Node:(9,0)isDiscovered():false父节点:(9,1)邻居:

生成邻居节点:(9,0)isDiscovered():true父节点:(9,1)邻居:(9,1)true

Popped-Node:(9,4)isDiscovered():false父节点:(9,3)邻居:

生成邻居节点:(9,4)isDiscovered():true父节点:(9,3)邻居:(9,5)false(9,3)true

Pushed-Node:(9,5)isDiscovered():false父节点:(9,4)邻居:

Popped-Node:(9,5)isDiscovered():false父节点:(9,4)邻居:

生成邻居节点:(9,5)isDiscovered():true父节点:(9,4)邻居:(9,6)false(9,4)true

Pushed-Node:(9,6)isDiscovered():false父节点:(9,5)邻居:

Popped-Node:(9,6)isDiscovered():false父节点:(9,5)邻居:

1 个答案:

答案 0 :(得分:1)

而不是推送邻居/子节点的所有,您应该只需要重新访问需要重新访问的节点,然后确定当该节点被弹出时哪个是下一个邻居/子节点关闭。通过在同一堆栈上保留要访问的节点和要重新访问的节点,难怪在某些时候它们会混淆。