簡體   English   中英

優化二維陣列尋路算法

[英]Optimizing a 2D array pathfinding algorithm

我試圖找到通過 map 的最短路徑,表示為 0 和 1 的矩陣,其中 0 是可通過的空間,1 是不可通過的牆壁。 入口在左上角 (0, 0),出口在右下角 (width - 1, height - 1)。

我的 function solution(int[][] map)找到從入口到出口的最短路徑的長度,您可以在該路徑中移除一堵牆作為路徑的一部分。 路徑長度是您通過的節點總數,包括入口節點和出口節點。 map的條件包括起始位置和結束位置始終可以通過 (0),map 將始終可以解決,盡管您可能需要也可能不需要移除牆壁,並且 Z1D78DC8ED51 的高度和寬度可以從 2 到 2214E51AEFEZB5114 20. 只能在大方向上移動; 不允許對角線移動。

幾個測試用例:

輸入:

int[][] a = {{0, 1, 1, 0}, 
             {0, 0, 0, 1}, 
             {1, 1, 0, 0}, 
             {1, 1, 1, 0}, 
             {1, 1, 1, 0}};

Output:

8

輸入:

int[][] b = {{0, 1, 0, 0, 0}, 
             {0, 0, 0, 1, 0}, 
             {0, 0, 1, 1, 0}, 
             {0, 1, 1, 0, 0},
             {0, 1, 1, 0, 0}};

Output:

9

輸入:

int[][] c = {{0, 1, 0, 0, 0}, 
             {0, 0, 0, 1, 0}, 
             {0, 0, 1, 1, 1}, 
             {0, 1, 1, 0, 0},
             {0, 1, 1, 0, 0}};

Output:

11

我的解決方案代碼如下:

public static int solution(int[][] map) {
    int rows = map.length; 
    int cols = map[0].length; 

    Graph graph = new Graph(map);

    Queue<Node> queue = new LinkedList<>();
    ArrayList<Node> marked = new ArrayList<>();
    Node start = graph.getNode(0, 0);

    queue.add(start);
    marked.add(start);
    start.setDistanceFromStart(1);

    while(!queue.isEmpty()) {
        Node current = queue.remove();

        if(current.getX() == rows - 1 && current.getY() == cols - 1) {  //if we have reached goal node
            return current.getDistanceFromStart();
        }

        for(Node n : current.getNeighbors()) {
            queue.add(n);
            n.setDistanceFromStart(current.getDistanceFromStart() + 1);

        }
    }
    return -1; //no path found
}

我創建了兩個類以及解決方法,Graph 和 Node:

class Graph{
    private Node[][] _nodeMap;

    private int _rows;
    private int _cols;

    public Graph(int[][] map) {
        _nodeMap = new Node[map.length][map[0].length];
        _rows = _nodeMap.length;
        _cols = _nodeMap[0].length;

        for (int i = 0; i < _rows; i++) {
            for(int j = 0; j < _cols; j++) {
                _nodeMap[i][j] = new Node(i, j, map[i][j], false, this);
            }
        }
    }

    /**
     * Gets the Node at location (x,y)
     * @param x - the x val of the Node being retrieved
     * @param y - the y val of the Node being retrieved 
     * @return
     */
    public Node getNode(int x, int y) {
        return _nodeMap[x][y];
    }

    /**
     * Replace the node at x,y with the new node n.
     * @param x
     * @param y
     * @param n
     */
    public void setNode(int x, int y, Node n) {
        _nodeMap[x][y] = n;
    }

    /**
     * Checks to see if a node has a neighbor (either a wall or a path) to the north 
     * @param n - the node being checked
     * @return boolean - true if there is a neighbor, false if there is not
     */
    public boolean hasNorth(Node n) {
        if(n.getX() > 0) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * Return's the neighbor to the north
     * @param n - the current node
     * @return Returns the Node to the north of n
     */
    public Node getNorth(Node n) {
        return _nodeMap[n.getX() - 1][n.getY()];
    }

    /**
     * Checks to see if a node has a neighbor (either a wall or a path) to the south 
     * @param n - the node being checked
     * @return boolean - true if there is a neighbor, false if there is not
     */
    public boolean hasSouth(Node n) {
        if(n.getX() < _rows - 1) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Return's the neighbor to the south
     * @param n - the current node
     * @return Returns the Node to the south of n
     */
    public Node getSouth(Node n) {
        return _nodeMap[n.getX() + 1][n.getY()];
    }

    /**
     * Checks to see if a node has a neighbor (either a wall or a path) to the east 
     * @param n - the node being checked
     * @return boolean - true if there is a neighbor, false if there is not
     */
    public boolean hasEast(Node n) {
        if(n.getY() < _cols - 1) {
            return true;
        } else {
        return false;
        }
    }

    /**
     * Return's the neighbor to the east
     * @param n - the current node
     * @return Returns the Node to the east of n
     */
    public Node getEast(Node n) {
        return _nodeMap[n.getX()][n.getY() + 1];
    }

    /**
     * Checks to see if a node has a neighbor (either a wall or a path) to the west 
     * @param n - the node being checked
     * @return boolean - true if there is a neighbor, false if there is not
     */
    public boolean hasWest(Node n) {
        if(n.getY() > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Return's the neighbor to the west
     * @param n - the current node
     * @return Returns the Node to the west of n
     */
    public Node getWest(Node n) {
        return _nodeMap[n.getX()][n.getY() - 1];
    }
}



class Node {
    private int _x; //x location
    private int _y; //y location
    private int _type; //1 if a wall, 0 if a path
    private Graph _map;
    private int _distFromStart;
    private boolean _wallRemoved;

    public Node(int x, int y, int type, boolean wallRemoved, Graph map){
        _x = x;
        _y = y;
        _type = type;
        _wallRemoved = wallRemoved;
        _map = map;
        _distFromStart = -1;
    }

    public int getX() {
        return _x;
    }

    public int getY() {
        return _y;
    }

    public int getType() {
        return _type;
    }

    public boolean getWallRemoved() {
        return _wallRemoved;
    }

    public int getDistanceFromStart() {
        return _distFromStart;
    }

    public void setDistanceFromStart(int distance) {
        _distFromStart = distance;
    }

    /**
     * Returns an ArrayList<Node> containing the neighbors of a node.
     * @return
     */
    public ArrayList<Node> getNeighbors(){
        ArrayList<Node> neighbors = new ArrayList<>();

        if(this._wallRemoved) { //if a wall has already been removed
            if(_map.hasWest(this) && _map.getWest(this)._type == 0) { //check west neighbor
                Node node = _map.getWest(this);
                Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map/*, node._timesEvaluated + 1*/);
                neighbors.add(n);
            }

            if(_map.hasEast(this) && _map.getEast(this)._type == 0) { //check east neighbor
                Node node = _map.getEast(this);
                Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map);
                neighbors.add(n);
            }

            if(_map.hasNorth(this) && _map.getNorth(this)._type == 0) { //check north neighbor
                Node node = _map.getNorth(this);
                Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map);
                neighbors.add(n);
            }

            if(_map.hasSouth(this) && _map.getSouth(this)._type == 0) { //check south neighbor
                Node node = _map.getSouth(this);
                Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map/);
                neighbors.add(n);
            }

        } else { //if a wall hasn't been removed yet

            if(_map.hasWest(this)) { //check west neighbor
                if(_map.getWest(this)._type == 1) { //if west neighbor is a wall
                    if(!this._wallRemoved) {
                        Node node = _map.getWest(this);
                        Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map);
                        neighbors.add(n);
                    }
                } else { //if west neighbor is a path
                    Node node = _map.getWest(this);
                    Node n = new Node(node.getX(), node.getY(), node.getType(), false, _map);
                    neighbors.add(n);
                }
            }

            if(_map.hasEast(this)) { //check east neighbor
                if(_map.getEast(this)._type == 1) {
                    if(!this._wallRemoved) {
                        Node node = _map.getEast(this);
                        Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map);
                        neighbors.add(n);
                    }
                } else {
                    Node node = _map.getEast(this);
                    Node n = new Node(node.getX(), node.getY(), node.getType(), false, _map);
                    neighbors.add(n);
                }
            }


            if(_map.hasNorth(this)) { //check north neighbor
                if(_map.getNorth(this)._type == 1) {
                    if(!this._wallRemoved) {
                        Node node = _map.getNorth(this);
                        Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map);
                        neighbors.add(n);
                    }
                } else {
                    Node node = _map.getNorth(this);
                    Node n = new Node(node.getX(), node.getY(), node.getType(), false, _map);
                    neighbors.add(n);
                }
            }

            if(_map.hasSouth(this)) { //check south neighbor
                if(_map.getSouth(this)._type == 1) {
                    if(!this._wallRemoved) {
                        Node node = _map.getSouth(this);
                        Node n = new Node(node.getX(), node.getY(), node.getType(), true, _map);
                        neighbors.add(n);
                    }
                } else {
                    Node node = _map.getSouth(this);
                    Node n = new Node(node.getX(), node.getY(), node.getType(), false, _map);
                    neighbors.add(n);
                }
            }
        }
        return neighbors;
    }
}

我的代碼有效,為每個測試用例返回正確答案。 我的問題是代碼非常慢。 我很確定迷宮是通過蠻力解決的(如果不是,時間限制至少類似於蠻力)導致大型地圖(例如 20x20 地圖)需要很長時間才能解決。 我怎樣才能優化我的代碼以使其運行得更快? 謝謝!

你有這個 ArrayList 稱為“標記”。 我建議使用它;)

for(Node n : current.getNeighbors()) {
    if(!marked.contains(n)){
        queue.add(n);
        n.setDistanceFromStart(current.getDistanceFromStart() + 1);
        marked.add(n);
    }
}

這將復雜度降低到 O(n * m),其中 n,m 是網格的尺寸。

另請閱讀關於 2d 空間中的 BFS 算法。 祝你好運:)

編輯#1

如果您想進一步改進代碼,請嘗試檢查A* algorithm

此外,您可以使用特殊參數將所有“進行中”節點保留在 PriorityQueue 上,而不是隊列,該參數將嘗試選擇最佳節點。 該參數可以是節點和(寬度 - 1,高度 - 1)(簡單畢達哥拉斯定理)之間的笛卡爾平面中的距離。 再次祝你好運:)

我認為這對普林斯頓大學的聯合查找會有所幫助https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM