簡體   English   中英

未加權圖的最短路徑(最少節點)

[英]Shortest path (fewest nodes) for unweighted graph

我正在嘗試構建一個方法,在未加權的圖形中返回從一個節點到另一個節點的最短路徑。 我考慮過使用Dijkstra,但這似乎有點矯枉過正,因為我只需要一對。 相反,我已經實現了廣度優先搜索,但問題是我的返回列表包含一些我不想要的節點 - 如何修改我的代碼以實現我的目標?

public List<Node> getDirections(Node start, Node finish){
    List<Node> directions = new LinkedList<Node>();
    Queue<Node> q = new LinkedList<Node>();
    Node current = start;
    q.add(current);
    while(!q.isEmpty()){
        current = q.remove();
        directions.add(current);
        if (current.equals(finish)){
            break;
        }else{
            for(Node node : current.getOutNodes()){
                if(!q.contains(node)){
                    q.add(node);
                }
            }
        }
    }
    if (!current.equals(finish)){
        System.out.println("can't reach destination");
    }
    return directions;
}

實際上你的代碼不會在循環圖中完成,考慮圖1 - > 2 - > 1.你必須有一個數組,你可以標記你已經訪問過哪個節點。 並且對於每個節點,您可以保存您來自的先前節點。 所以這里是正確的代碼:

private Map<Node, Boolean>> vis = new HashMap<Node, Boolean>();

private Map<Node, Node> prev = new HashMap<Node, Node>();

public List getDirections(Node start, Node finish){
    List directions = new LinkedList();
    Queue q = new LinkedList();
    Node current = start;
    q.add(current);
    vis.put(current, true);
    while(!q.isEmpty()){
        current = q.remove();
        if (current.equals(finish)){
            break;
        }else{
            for(Node node : current.getOutNodes()){
                if(!vis.contains(node)){
                    q.add(node);
                    vis.put(node, true);
                    prev.put(node, current);
                }
            }
        }
    }
    if (!current.equals(finish)){
        System.out.println("can't reach destination");
    }
    for(Node node = finish; node != null; node = prev.get(node)) {
        directions.add(node);
    }
    directions.reverse();
    return directions;
}

謝謝Giolekva!

我重寫了它,重構了一些:

  • 訪問節點的集合不必是地圖。
  • 對於路徑重建,可以查找下一個節點,而不是前一個節點,從而無需反轉方向。
public List<Node> getDirections(Node sourceNode, Node destinationNode) {
    //Initialization.
    Map<Node, Node> nextNodeMap = new HashMap<Node, Node>();
    Node currentNode = sourceNode;

    //Queue
    Queue<Node> queue = new LinkedList<Node>();
    queue.add(currentNode);

    /*
     * The set of visited nodes doesn't have to be a Map, and, since order
     * is not important, an ordered collection is not needed. HashSet is 
     * fast for add and lookup, if configured properly.
     */
    Set<Node> visitedNodes = new HashSet<Node>();
    visitedNodes.add(currentNode);

    //Search.
    while (!queue.isEmpty()) {
        currentNode = queue.remove();
        if (currentNode.equals(destinationNode)) {
            break;
        } else {
            for (Node nextNode : getChildNodes(currentNode)) {
                if (!visitedNodes.contains(nextNode)) {
                    queue.add(nextNode);
                    visitedNodes.add(nextNode);

                    //Look up of next node instead of previous.
                    nextNodeMap.put(currentNode, nextNode);
                }
            }
        }
    }

    //If all nodes are explored and the destination node hasn't been found.
    if (!currentNode.equals(destinationNode)) {
        throw new RuntimeException("No feasible path.");
    }

    //Reconstruct path. No need to reverse.
    List<Node> directions = new LinkedList<Node>();
    for (Node node = sourceNode; node != null; node = nextNodeMap.get(node)) {
        directions.add(node);
    }

    return directions;
}

得到一對的答案比對所有對都簡單得多。 計算最短路徑的常用方法是像您一樣開始,但只要遇到新節點並在路徑上記錄上一個節點就做一個注釋。 然后,當您到達目標節點時,您可以跟蹤到源的反向鏈接並獲取路徑。 因此,從循環中刪除directions.add(current) ,並添加類似以下內容的代碼

Map<Node,Node> backlinks = new HashMap<Node,Node>();

在開始然后在循環中

if (!backlinks.containsKey(node)) {
    backlinks.add(node, current);
    q.add(node);
}

然后最后,使用backlinks映射向后構建directions列表。

每次通過循環,你都會打電話

directions.Add(current);

相反,你應該把它移到你真正知道你想要那個條目的地方。

將它們放入隊列時,必須將父節點包含在每個節點中。 然后,您可以遞歸地從該列表中讀取路徑。

假設您要在此圖中找到從A到D的最短路徑:

     /B------C------D
   /                |
 A                 /
   \             /
     \E---------

每次排隊節點時,都要跟蹤到達此處的方式。 因此,在步驟1 B(A)中,E(A)被放在隊列中。 在第二步中B出隊並且C(B)被放入隊列等。然后通過“向后”遞歸,它很容易找到回來的路。

最好的方法可能是制作一個數組,只要有節點並保持鏈接在那里(這通常是在Dijkstra's中完成的)。

暫無
暫無

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

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