[數據結構]迪傑斯特拉(Dijkstra)算法


基本思想

     通過Dijkstra計算圖G中的最短路徑時,需要指定起點vs(即從頂點vs開始計算)。

     此外,引進兩個集合S和U。S的作用是記錄已求出最短路徑的頂點,而U則是記錄還未求出最短路徑的頂點(以及該頂點到起點vs的距離)。

     初始時,S中只有起點vs;U中是除vs之外的頂點,並且U中頂點的路徑是"起點vs到該頂點的路徑"。然后,從U中找出路徑最短的頂點,並將其加入到S中;接着,更新U中的頂點和頂點對應的路徑。 然后,再從U中找出路徑最短的頂點,並將其加入到S中;接着,更新U中的頂點和頂點對應的路徑。 ... 重復該操作,直到遍歷完所有頂點。

操作步驟

(1) 初始時,S只包含起點vs;U包含除vs外的其他頂點,且U中頂點的距離為"起點vs到該頂點的距離"[例如,U中頂點v的距離為(vs,v)的長度,然后vs和v不相鄰,則v的距離為∞]。

(2) 從U中選出"距離最短的頂點k",並將頂點k加入到S中;同時,從U中移除頂點k。

(3) 更新U中各個頂點到起點vs的距離。之所以更新U中頂點的距離,是由於上一步中確定了k是求出最短路徑的頂點,從而可以利用k來更新其它頂點的距離;例如,(vs,v)的距離可能大於(vs,k)+(k,v)的距離。

(4) 重復步驟(2)和(3),直到遍歷完所有頂點。

 

接下來做一個簡單例子求解:

 

 

package com.darrenchan.graph;

import java.util.ArrayList;
import java.util.List;

public class ShortestPathDijkstra {
    /** 鄰接矩陣 */
    private int[][] matrix;
    /** 表示正無窮 */
    private int MAX_WEIGHT = Integer.MAX_VALUE;
    /** 頂點集合 */
    private String[] vertexes;

    /**
     * 創建圖
     */
    private void createGraph(int index) {
        matrix = new int[index][index];
        vertexes = new String[index];

        int[] v0 = { 0, 1, 5, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] v1 = { 1, 0, 3, 7, 5, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] v2 = { 5, 3, 0, MAX_WEIGHT, 1, 7, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] v3 = { MAX_WEIGHT, 7, MAX_WEIGHT, 0, 2, MAX_WEIGHT, 3, MAX_WEIGHT, MAX_WEIGHT };
        int[] v4 = { MAX_WEIGHT, 5, 1, 2, 0, 3, 6, 9, MAX_WEIGHT };
        int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, 7, MAX_WEIGHT, 3, 0, MAX_WEIGHT, 5, MAX_WEIGHT };
        int[] v6 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 3, 6, MAX_WEIGHT, 0, 2, 7 };
        int[] v7 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 9, 5, 2, 0, 4 };
        int[] v8 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 7, 4, 0 };
        matrix[0] = v0;
        matrix[1] = v1;
        matrix[2] = v2;
        matrix[3] = v3;
        matrix[4] = v4;
        matrix[5] = v5;
        matrix[6] = v6;
        matrix[7] = v7;
        matrix[8] = v8;

        vertexes[0] = "v0";
        vertexes[1] = "v1";
        vertexes[2] = "v2";
        vertexes[3] = "v3";
        vertexes[4] = "v4";
        vertexes[5] = "v5";
        vertexes[6] = "v6";
        vertexes[7] = "v7";
        vertexes[8] = "v8";
    }

    /**
     * Dijkstra最短路徑。
     *
     * vs -- 起始頂點(start vertex) 即,統計圖中"頂點vs"到其它各個頂點的最短路徑。
     */
    public void dijkstra(int vs) {
        // flag[i]=true表示"頂點vs"到"頂點i"的最短路徑已成功獲取
        boolean[] flag = new boolean[vertexes.length];
        // U則是記錄還未求出最短路徑的頂點(以及該頂點到起點s的距離),與 flag配合使用,flag[i] == true 表示U中i頂點已被移除
        int[] U = new int[vertexes.length];
        // 前驅頂點數組,即,prev[i]的值是"頂點vs"到"頂點i"的最短路徑所經歷的全部頂點中,位於"頂點i"之前的那個頂點。
        int[] prev = new int[vertexes.length];
        // S的作用是記錄已求出最短路徑的頂點
        String[] S = new String[vertexes.length];

        // 步驟一:初始時,S中只有起點vs;U中是除vs之外的頂點,並且U中頂點的路徑是"起點vs到該頂點的路徑"。
        for (int i = 0; i < vertexes.length; i++) {
            flag[i] = false; // 頂點i的最短路徑還沒獲取到。
            U[i] = matrix[vs][i]; // 頂點i與頂點vs的初始距離為"頂點vs"到"頂點i"的權。也就是鄰接矩陣vs行的數據。

            prev[i] = 0; //頂點i的前驅頂點為0
        }

        // 將vs從U中“移除”(U與flag配合使用)
        flag[vs] = true;
        U[vs] = 0;
        // 將vs頂點加入S
        S[0] = vertexes[vs];
        // 步驟一結束

        //步驟四:重復步驟二三,直到遍歷完所有頂點。
        // 遍歷vertexes.length-1次;每次找出一個頂點的最短路徑。
        int k = 0;
        for (int i = 1; i < vertexes.length; i++) {
            // 步驟二:從U中找出路徑最短的頂點,並將其加入到S中(如果vs頂點到x頂點還有更短的路徑的話,那么
            // 必然會有一個y頂點到vs頂點的路徑比前者更短且沒有加入S中
            // 所以,U中路徑最短頂點的路徑就是該頂點的最短路徑)
            // 即,在未獲取最短路徑的頂點中,找到離vs最近的頂點(k)。
            int min = MAX_WEIGHT;
            for (int j = 0; j < vertexes.length; j++) {
                if (flag[j] == false && U[j] < min) {
                    min = U[j];
                    k = j;
                }
            }

            //將k放入S中
            S[i] = vertexes[k];

            //步驟二結束


            //步驟三:更新U中的頂點和頂點對應的路徑
            //標記"頂點k"為已經獲取到最短路徑(更新U中的頂點,即將k頂點對應的flag標記為true)
            flag[k] = true;

            //修正當前最短路徑和前驅頂點(更新U中剩余頂點對應的路徑)
            //即,當已經"頂點k的最短路徑"之后,更新"未獲取最短路徑的頂點的最短路徑和前驅頂點"。
            for (int j = 0; j < vertexes.length; j++) {
                //以k頂點所在位置連線其他頂點,判斷其他頂點經過最短路徑頂點k到達vs頂點是否小於目前的最短路徑,是,更新入U,不是,不做處理
                int tmp = (matrix[k][j] == MAX_WEIGHT ? MAX_WEIGHT : (min + matrix[k][j]));
                if (flag[j] == false && (tmp < U[j])) {
                    U[j] = tmp;
                    //更新 j頂點的最短路徑前驅頂點為k
                    prev[j] = k;
                }
            }
            //步驟三結束
        }
        //步驟四結束

        // 打印dijkstra最短路徑的結果
        System.out.println("起始頂點:" + vertexes[vs]);
        for (int i = 0; i < vertexes.length; i++) {
            System.out.print("最短路徑(" + vertexes[vs] + "," + vertexes[i] + "):" + U[i] + "  ");

            List<String> path = new ArrayList<>();
            int j = i;
            while (true) {
                path.add(vertexes[j]);

                if (j == 0)
                    break;

                j = prev[j];
            }

            for (int x = path.size()-1; x >= 0; x--) {
                if (x == 0) {
                    System.out.println(path.get(x));
                } else {
                    System.out.print(path.get(x) + "->");
                }
            }

        }

        System.out.println("頂點放入S中的順序:");
        for (int i = 0; i< vertexes.length; i++) {

            System.out.print(S[i]);

            if (i != vertexes.length-1)
                System.out.print("-->");
        }

    }

    public static void main(String[] args) {
        ShortestPathDijkstra dij = new ShortestPathDijkstra();
        dij.createGraph(9);
        dij.dijkstra(0);
    }

}

 

參考:https://blog.csdn.net/CmdSmith/article/details/56839285


免責聲明!

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



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