算法練習(19)-單源最短路徑dijkstra算法


如上圖,先初始化1個圖,每條邊上的紅色數字為路徑權重:(Node,Edge的定義參見算法練習(17)-圖的廣度優先遍歷/深度優先遍歷

Graph init() {
        List<Node> nodes = new ArrayList<>();
        List<Edge> edges = new ArrayList<>();

        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);

        nodes.add(n1);
        nodes.add(n2);
        nodes.add(n3);
        nodes.add(n4);
        nodes.add(n5);

        Edge e_1_2 = new Edge(1, n1, n2);
        Edge e_2_1 = new Edge(1, n2, n1);
        Edge e_1_3 = new Edge(7, n1, n3);
        Edge e_3_1 = new Edge(7, n3, n1);
        Edge e_1_4 = new Edge(8, n1, n4);
        Edge e_4_1 = new Edge(8, n4, n1);
        Edge e_2_3 = new Edge(5, n2, n3);
        Edge e_3_2 = new Edge(5, n3, n2);
        Edge e_3_4 = new Edge(10, n3, n4);
        Edge e_4_3 = new Edge(10, n4, n3);
        Edge e_2_5 = new Edge(20, n2, n5);
        Edge e_5_2 = new Edge(20, n5, n2);
        Edge e_5_4 = new Edge(9, n5, n4);
        Edge e_4_5 = new Edge(9, n4, n5);
        Edge e_3_5 = new Edge(6, n3, n5);
        Edge e_5_3 = new Edge(6, n5, n3);

        n1.edges.add(e_1_2);
        n1.edges.add(e_1_3);
        n1.edges.add(e_1_4);

        n2.edges.add(e_2_1);
        n2.edges.add(e_2_3);
        n2.edges.add(e_2_5);

        n3.edges.add(e_3_1);
        n3.edges.add(e_3_2);
        n3.edges.add(e_3_4);
        n3.edges.add(e_3_5);

        n4.edges.add(e_4_1);
        n4.edges.add(e_4_3);
        n4.edges.add(e_4_5);

        n5.edges.add(e_5_2);
        n5.edges.add(e_5_3);
        n5.edges.add(e_5_4);

        edges.add(e_1_2);
        edges.add(e_2_1);
        edges.add(e_1_3);
        edges.add(e_3_1);
        edges.add(e_1_4);
        edges.add(e_4_1);
        edges.add(e_2_3);
        edges.add(e_3_2);
        edges.add(e_3_4);
        edges.add(e_4_3);
        edges.add(e_2_5);
        edges.add(e_5_2);
        edges.add(e_5_4);
        edges.add(e_4_5);
        edges.add(e_3_5);
        edges.add(e_5_3);

        Graph g = new Graph(nodes, edges);
        return g;
    }

假設從節點1出發,到達其它節點的最短路徑(權重)為:

出發點 目的地 最短路徑(權重)和 全路徑
1 1 0 1->1
1 2 1 1->2
1 3 1+5 1->2->3
1 4 8 1->4
1 5 1+5+6 1->2->3->5
package advanced;

import java.util.*;

public class GraphTest {


    Graph init() {
        ... 略...
    }


    /**
     * dijkstra算法
     * @param head
     * @return
     */
    Map<Node, Integer> dijkstra(Node head) {
        /**
         * 用於保存從head到其它node的距離總和
         * 不在該map中節點,表示還沒走到,默認距離為正無窮
         */
        Map<Node, Integer> distanceMap = new HashMap<>();
        //首節點:從head到head的距離為0
        distanceMap.put(head, 0);
        //已經計算過的節點
        Set<Node> selectedNodes = new HashSet<>();
        //從出發點,找出距離最短的節點
        Node minNode = getMinDistanceNode(distanceMap, selectedNodes);
        while (minNode != null) {
            int distance = distanceMap.get(minNode);
            for (Edge edge : minNode.edges) {
                Node toNode = edge.to;
                if (!distanceMap.containsKey(toNode)) {
                    distanceMap.put(toNode, distance + edge.weight);
                }
                //取最短距離,更新distanceMap
                distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight));
            }
            //已計算過的節點,做下標識
            selectedNodes.add(minNode);
            minNode = getMinDistanceNode(distanceMap, selectedNodes);
        }
        return distanceMap;
    }

    /**
     * 從distanceMap中找出最小距離的節點(已計算過的節點忽略)
     * @param distanceMap
     * @param touchedNodes
     * @return
     */
    Node getMinDistanceNode(Map<Node, Integer> distanceMap,
                            Set<Node> touchedNodes) {
        Node minNode = null;
        int minDistance = Integer.MAX_VALUE;
        for (Map.Entry<Node, Integer> entry : distanceMap.entrySet()) {
            Node node = entry.getKey();
            int distance = entry.getValue();
            if (!touchedNodes.contains(node) && distance < minDistance) {
                minNode = node;
                minDistance = distance;
            }
        }
        return minNode;
    }


    public static void main(String[] args) {
        GraphTest t = new GraphTest();
        Graph g = t.init();
        Map<Node, Integer> dijkstra = t.dijkstra(g.nodes.get(0));
        System.out.println(dijkstra);
    }

}

輸出:

{3=6, 4=8, 1=0, 5=12, 2=1}

注意:這個算法,有一個前提條件,如果圖中有環,環上的路徑合不能為負值,否則會在環里轉來轉去,每轉一圈,路徑合更小,一直循環,轉不出來。

如上圖,如果從1出發,要計算到節點2的最短路徑,每轉一圈,總路徑反而更短。這種情況下,可以將所有邊上的權重加“最大負權重”,將所有邊上的權重變成非負值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM