1. 程式人生 > >圖的使用場景和常用演算法

圖的使用場景和常用演算法

一.什麼是圖?有哪些特性及其使用場景?

   由來: 當我們需要表示多對多的關係的時候,就需要使用到圖這種資料結構

   定義: 圖是一種資料結構,其中頂點可以是具有零個或多個相鄰元素.兩個頂點之間的連線稱為邊,節點被稱為頂點

            

 

 

    常用概念:  無向圖表示頂點之間的連線沒有方向,既可以A->B,也可以B->A,有向圖表示頂點之間的連線有方向,A->B,B->A,表示不同的路徑

        

 

 

 

   圖的建立: 鄰接矩陣(使用二維陣列)和鄰接表(使用連結串列)

public class Graph {

    private List<String> vertexList;  // 儲存頂點的集合
    private int[][] edges;            // 儲存圖對應的鄰接矩陣
    private int numberOfEdges;        // 表示邊的數量
    private boolean[] isVisited;      // 是否被訪問過

    public static void main(String[] args) {
        Graph graph = new Graph(5);
        String[] vertexes = {"A","B","C","D","E"};
        // 新增節點
        for (String vertex : vertexes) {
            graph.insertVertex(vertex);
        }
        // 新增邊 A-B A-C B-C B-D B-E 用於建立鄰接矩陣
        graph.insertWeight(0,1,1);
        graph.insertWeight(0,2,1);
        graph.insertWeight(1,2,1);
        graph.insertWeight(1,3,1);
        graph.insertWeight(1,4,1);

        graph.showGraph();
    }

    /**
     * 構造器 ,初始化頂點,鄰接矩陣,邊的數目,是否訪問過
     * @param n
     */
    public Graph(int n) {
        vertexList = new ArrayList<String> (n);
        edges = new int[n][n];
        isVisited = new boolean[n];
        numberOfEdges = 0;
    }
}

 /**
     * 插入頂點
     * @param vertex
     */
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }

    /**
     * 新增邊 無向圖
     * @param v1        表示第一個頂點對應的下標
     * @param v2        表示第二個頂點對應的下標
     * @param weight    表示權值
     */
    public void insertWeight(int v1,int v2,int weight){
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numberOfEdges++;
    }

 執行結果:

 

 

 

二.圖的遍歷

    深度優先遍歷(dfs)

 基本思想:

     1.從初始訪問的節點出發,初始訪問的節點可能有多個鄰接節點,深度優先遍歷的策略是首先訪問第一個鄰接節點,然後在把這個被訪問過的鄰接節點

        作為初始節點,在訪問它的鄰接節點.即每次訪問完當前節點後首先訪問當前節點的第一個鄰接節點

      2.這樣的訪問策略是優先縱向的挖掘深入,而不是對所有的節點進行橫向的訪問

      3.這是一個遞迴呼叫的過程

 步驟:  

     1.訪問初始節點v,並將v標記為已訪問

     2.查詢節點v的第一個鄰接節點w

                  3.若w存在,繼續執行4,如果不存在,回到第1步,繼續從v的下一個節點繼續  

        4.若w未被訪問,對w進行深度優先訪問

        5.查詢節點v的w鄰接節點的下一個鄰接節點,轉到步驟3    

               

 

 

 

/**
     * 返回節點i對應的資料
     * @param i
     * @return
     */
    public String getValueByIndex(int i){
        return vertexList.get(i);
    }


/**
     * 找到第一個相鄰節點的下標
     * @param index
     * @return
     */
    public int getFirstNeighbor(int index){
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[index][j] != 0){
                return j;
            }
        }
        return -1;
    }

/**
     * 根據前一個相鄰節點的下標獲取下一個相鄰節點的下標
     * @param v1
     * @param v2
     * @return
     */
    public int getNextNeighbor(int v1,int v2){
        for (int j = v2+1; j < vertexList.size(); j++) {
            if (edges[v1][j] != 0) {
                return j;
            }
        }
        return -1;
    }

/**
     * 深度優先遍歷
     * @param isVisited
     * @param i
     */
    private void  dfs(boolean[] isVisited,int i){
        // 訪問當前的節點
        System.out.print(getValueByIndex(i) +"->");
        // 將被訪問的節點設定成已訪問
        isVisited[i] = true;
        // 獲取當前節點的相鄰的節點
        int w = getFirstNeighbor(i);
        while (w != -1){ // 只要當前節點的鄰接點不為空
            if (!isVisited[w]){ // 如果沒有訪問過
                dfs(isVisited, w); //繼續遞迴
            }
            // 繼續從它的下一個鄰接點開始執行
            w = getNextNeighbor(i,w);
        }
    }

 public void dfs(){
        isVisited = new boolean[vertexList.size()];
        for (int i = 0; i < vertexList.size(); i++) {
            if (!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }

執行結果:

  

 

 廣度優先遍歷(bfs)

  基本思想:

      類似於分層搜尋的過程,需要使用一個佇列來儲存訪問過的節點順序,以便按照這個順序來訪問這些節點的鄰接節點

  步驟:

      1.訪問初始節點v,並將v標記為已訪問

      2.節點v入佇列

      3.當佇列非空時,繼續執行,否則演算法結束

      4.出佇列,取出佇列頭u

      5.查詢u的第一個鄰接節點w

      6.若節點u的的鄰接節點w不存在,轉回步驟3,否則執行步驟7

      7.若節點w尚未被訪問,則將w標記為已訪問

      8.節點w入佇列

      9.查詢節點u的繼w的鄰接節點後的下一個鄰接節點,重複步驟6直到佇列為空

/**
     * 廣度優先遍歷
     * @param isVisited
     * @param i
     */
    private void bfs(boolean[] isVisited, int i){
        int u; // 表示佇列頭結點對應的下標
        int w; // 鄰接節點w
        // 用於記錄節點訪問的順序
        Queue<Integer> queue = new LinkedList<>();
        // 訪問節點,輸出節點的值
        System.out.print(getValueByIndex(i) + "->");
        // 將已訪問的節點標記為已訪問
        isVisited[i] = true;
        // 將已訪問的節點加入到佇列
        queue.add(i);
        while (!queue.isEmpty()){
            // 取出佇列頭節點的下標
            u = queue.remove();
            // 得到其鄰接節點的下標
            w = getFirstNeighbor(u);
            while (w != -1){ // 如果鄰接節點存在
                if(!isVisited[w]){ // 是否已經訪問過該節點
                    System.out.print(getValueByIndex(w) + "->"); // 訪問該節點
                    isVisited[w] = true; // 將該節點的狀態設定為已訪問
                    queue.add(w); // 加入到佇列中
                }
                w = getNextNeighbor(u,w); //以u為前驅節點,找到其下一個節點
            }
        }
    }

public void bfs(){
        isVisited = new boolean[vertexList.size()];
        for (int i = 0; i < vertexList.size(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited,i);
            }
        }
    }

 執行結果:

 三.求解圖的最小生成樹

  什麼是最小生成樹?

    1.給定一個帶權的無向連通圖,如何選取一顆生成樹,使得樹上所有邊上權的總和為最小,就叫最小生成樹

    2.有N個頂點,一定會有N-1條邊,並且包含全部的頂點

                     

 如何求得最小生成樹

   1.普里姆演算法

    步驟:

      1.設G=(V,E)是聯通網,T=(U,D)是最小生成樹,V,U是頂點集合,E,D是邊的集合

      2.若從頂點u開始構建最小生成樹,則從集合V中取出頂點u放入到集合U中,並標記頂點v為已訪問

      3.若集合U中的頂點ui和集合U-V的頂點vj之間存在邊,則尋找這些邊權值的最小邊,但不能構成迴路,將頂點vj加入集合U中,標記頂點v已訪問

      4.重複步驟2,直到集合V和U相等,並且所有的頂點都標記為已訪問,此時D中就有n-1條邊

                    

 

   建立圖物件並初始化

class MGraph{
    int vertexes; // 圖的節點個數
    char[] data;  // 存放頂點座標  
    int[][] weight;// 存放邊,即鄰接矩陣

    public MGraph(int vertexes) {
        this.vertexes = vertexes;
        data = new char[vertexes];
        weight = new int[vertexes][vertexes];
    }

}
class MinTree {
    /**
     * 建立圖物件
     * @param graph
     * @param vertexes
     * @param data
     * @param weight
     */
    public void createGraph(MGraph graph,int vertexes,char[] data,int[][] weight){
        for (int i = 0; i < vertexes; i++) {
            graph.data[i] = data[i];
            for (int j = 0; j < vertexes; j++){
                graph.weight[i][j] = weight[i][j];
            }
        }
    }

    /**
     * 顯示圖的鄰接矩陣
     * @param graph
     */
    public  void show(MGraph graph){
        for(int[] link:graph.weight){
            System.out.println(Arrays.toString(link));
        }
    }
}
 public static void main(String[] args) {
        char[] data = {'A','B','C','D','E','F','G'};
        int vertexes = data.length;
        // 使用10000表示兩條線之間不連通
        int[][] weight = {
                {10000,5,7,10000,10000,10000,2},
                {5,10000,10000,9,10000,10000,3},
                {7,10000,10000,10000,8,10000,10000},
                {10000,9,10000,10000,10000,4,10000},
                {10000,10000,8,10000,10000,5,4},
                {10000,10000,10000,4,5,10000,6},
                {2,3,10000,10000,4,6,10000},};

        MinTree minTree = new MinTree();
        MGraph graph = new MGraph(vertexes);
        minTree.createGraph(graph,vertexes,data,weight);
        minTree.show(graph);
    }
}

執行結果:

      

 

   實現普利姆演算法,假設從G點開始走

/**
     * 普利姆演算法
     * @param graph 圖物件
     * @param v     從哪個頂點開始
     */
    public void prim(MGraph graph,int v){
        // 存放訪問過的節點
        int[] visited = new int[graph.vertexes];
        //初始化visited的值
        for (int i = 0; i < graph.vertexes;i++){
            visited[i] = 0;
        }
        // 把當前的節點標記為已訪問
        visited[v] = 1;
        // h1和h2 記錄兩個頂點的下標
        int h1 = -1;
        int h2 = -1;
        // 把minWeight設定為一個最大值,表示兩個點不能連通,後續會替換
        int minWeight = 10000;

        for (int k = 1; k < graph.vertexes;k++){ // 因為有vertexes個頂點,所以結束後,會生成vertexes-1條邊,邊的數量
            for (int i = 0; i < graph.vertexes;i++){ //  i表示已經訪問過的節點
                for (int j = 0; j < graph.vertexes;j++){// j表示未訪問過的節點
                    if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight){ // 尋找已訪問的節點和未訪問過的節點間的權值最小的邊
                        minWeight = graph.weight[i][j]; // 將minWeight的值更新為圖的權重值
                        h1 = i;                // h1更新為已訪問
                        h2 = j;               //  h2更新為已訪問 
                    }
                }
            }
            System.out.println("<" + graph.data[h1] + ", "+graph.data[h2] + "> 權值:"+ minWeight);
            // 把當前的節點設定為已訪問
            visited[h2] = 1;
            // 重置minWeight的值
            minWeight = 10000;
        }
    }

執行結果:

    

 

 2.克魯斯卡爾演算法

     步驟:

    1.按照權值從到到小進行排序

    2.保證這些邊不構成迴路

  1.建立圖

public class KruskalDemo {

    private int edgeNums;  // 邊的數量
    private char[] vertexes; // 頂點的集合
    private int[][] matrix;  // 鄰接矩陣
    private static final int INF = Integer.MAX_VALUE;// 表示兩點之間不能聯通


    public static void main(String[] args) {
        char[] vertexes = {'A','B','C','D','E','F','G'};
        int matrix[][] = {
                /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
                /*A*/ {   0,  12, INF, INF, INF,  16,  14},
                /*B*/ {  12,   0,  10, INF, INF,   7, INF},
                /*C*/ { INF,  10,   0,   3,   5,   6, INF},
                /*D*/ { INF, INF,   3,   0,   4, INF, INF},
                /*E*/ { INF, INF,   5,   4,   0,   2,   8},
                /*F*/ {  16,   7,   6, INF,   2,   0,   9},
                /*G*/ {  14, INF, INF, INF,   8,   9,   0}};


        KruskalDemo kruskal = new KruskalDemo(vertexes,matrix);
        kruskal.show();
    }

    /*初始化頂點和鄰接矩陣*/
    public KruskalDemo(char[] vertexes,int[][] matrix){
        // 構造方法
        int vlen = vertexes.length;

        //使用複製拷貝的方法初始化頂點
        this.vertexes = new char[vlen];
        for (int i = 0; i < vertexes.length; i++){
            this.vertexes[i] = vertexes[i];
        }

        // 使用複製拷貝的方法,初始化邊(權值)
        this.matrix = new int[vlen][vlen];
        for (int i = 0; i < vertexes.length; i++){
            for (int j = 0; j < vertexes.length; j++){
                this.matrix[i][j] = matrix[i][j];
            }
        }

        // 初始化有效邊的數量
        for (int i = 0; i < vertexes.length; i++){
            // 自己和自己不算有效邊
            for (int j = i+1; j < vertexes.length; j++){
                if (this.matrix[i][j] != INF){
                    edgeNums++;
                }
            }
        }

    }

    public void show(){
        for (int i = 0; i < vertexes.length;i++){
            for (int j = 0; j < vertexes.length; j++){
                System.out.printf("%d\t",matrix[i][j]);
            }
            System.out.println();
        }
    }
}

執行結果:

      

 

  建立邊,根據權值進行升序排列

class Edata implements Comparable<Edata>{
    public char start; // 邊的起始點
    public char end; // 邊的終點
    public int weight; // 邊的權值

    public Edata(char start, char end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Edata [<" + start +","+end+">= "+weight+"]";
    }

    @Override
    public int compareTo(Edata o) {
        // 升序排列
        return this.weight - o.weight;
    }
}

  判斷兩條邊是否是同一個終點,如果不是就加入到樹中,直到樹擴大成一個森林

/**
     * 返回一個頂點對應的下標值,找不到返回-1
     * @param ch
     * @return
     */
    private int getPosition(char ch){
        for (int i = 0; i < vertexes.length;i++){
            if (vertexes[i] == ch ){
                return i;
            }
        }
        return - 1;
    }

 /**
     * 獲取圖中的邊,放入到Edata陣列中,
     * @return
     */
    private Edata[] getEdges(){
        int index = 0;
        Edata[] edges = new Edata[edgeNums];
        for (int i = 0; i < vertexes.length;i++){
            for (int j = i+1; j < vertexes.length; j++) {
                if (matrix[i][j] != INF){
                    edges[index++] = new Edata(vertexes[i],vertexes[j],matrix[i][j]);
                }
            }
        }
        return edges;
    }

/**
     * 獲取下標為i頂點對應的終點,用於判斷兩個頂點的終點是否相同
     * @param ends 記錄了各個頂點對應的終點是哪個,在遍歷過程中,逐步形成
     * @param i    傳入的頂點對應的下標
     * @return     返回這個頂點的終點對應的下標
     */
    private int getEnd(int[] ends,int i){
        while (ends[i] != 0){
            i = ends[i];
        }
        return i;
    }

   完成演算法

public void kruskal(){
        int index = 0; // 表示最後結果的陣列索引
        int[] ends = new int[edgeNums]; // 用於儲存已有最小生成樹中每個頂點的終點
        Edata[] rets = new Edata[edgeNums]; // 建立結果陣列,保留最後的最小生成樹

        Edata[] edges = getEdges(); // 獲取所有邊的集合
        Collections.sort(Arrays.asList(edges)); // 排序
       // System.out.println("圖的邊的集合=" + Arrays.toString(edges) + " 共"+ edges.length);
        // 將邊新增到最小生成樹中,同時判斷是否生成迴路
        for (int i = 0; i < edgeNums; i++){
            // 獲取第i條邊的第一個頂點
            int p1 = getPosition(edges[i].start);
            // 獲取第i條邊的第二個頂點
            int p2 = getPosition(edges[i].end);

            // 獲取p1這個頂點在已有最小生成樹中的終點
            int m = getEnd(ends,p1);
            // 獲取p2這個頂點在已有最小生成樹中的終點
            int n = getEnd(ends,p2);
            // 是否構成迴路
            if (m != n){ // 如果沒有構成迴路
                ends[m] = n; // 設定m在已有最小生成樹中的終點
                rets[index++] = edges[i]; // 將這條邊加入到結果陣列
            }
        }

        System.out.println("最小生成樹為:");
        for (int i = 0; i < index; i++){
            System.out.println(rets[i]);
        }
    }

執行結果:

           

 

 四.求出圖的最短路徑

  1.迪傑斯特拉演算法(從單一頂點出發到其他的各個頂點的最小路徑)

        

     步驟:

       1.設出發頂點為v,頂點集合V{v1,v2,...vi},v到V中各頂點集合的距離集合Dis,Dis{d1,d2,...di},Dis記錄了v到圖中各個頂點的距離(到自身可以看做是0,v到vi對應的距離是di)

          2.從Dis中選擇值最小的di並移出Dis集合,同時移除V集合對應的頂點vi,此時的v到vi即為最短路徑

        3.更新Dis集合,比較v到V集合中頂點的距離值,與v通過vi到V集合中頂點的距離值,保留較小的那個(同時也應該更新頂點的前驅節點為vi,表示通過vi達到的)

        4.重複步驟2和3,直到最短路徑頂點為目標頂點即可結束

  初始化圖

public static void main(String[] args) {
        char[] vertexes = {'A','B','C','D','E','F','G'};
        int[][] matrix = new int[vertexes.length][vertexes.length];
        final int N = 65535;// 表示不可以連線
        matrix[0]=new int[]{N,5,7,N,N,N,2};
        matrix[1]=new int[]{5,N,N,9,N,N,3};
        matrix[2]=new int[]{7,N,N,N,8,N,N};
        matrix[3]=new int[]{N,9,N,N,N,4,N};
        matrix[4]=new int[]{N,N,8,N,N,5,4};
        matrix[5]=new int[]{N,N,N,4,5,N,6};
        matrix[6]=new int[]{2,3,N,N,4,6,N};

        Graph g = new Graph(vertexes,matrix);
        g.showGraph();
      
    }
class Graph {
    public char[] vertexes;
    public int[][] matrix;
    public VisitedVertex vv;

    public Graph(char[] vertexes, int[][] matrix) {
        this.vertexes = vertexes;
        this.matrix = matrix;
    }

    public void showGraph(){
        for (int[] links:matrix){
            System.out.println(Arrays.toString(links));
        }
    }
}

 執行結果:

          

 

 

   初始化前驅節點,已訪問節點和距離

class VisitedVertex {

    /*記錄各個頂點是否訪問過,1表示已訪問,0表示未訪問*/
    public int[] already_arr;
    /*記錄每一個下標對應值的前一個下標,動態更新*/
    public int[] pre_visited;
    /*記錄出發頂點到各個頂點的距離*/
    public int[] dis;

    /**
     *
     * @param length 初始化頂點的個數
     * @param index  從哪個頂點開始
     */
    public VisitedVertex(int length,int index) {
        already_arr = new int[length];
        pre_visited = new int[length];
        dis = new int[length];
        /*初始化dis陣列*/
        Arrays.fill(dis,65535);
        /*設定除法頂點被訪問過*/
        this.already_arr[index] = 1;
        /*設定出發頂點的訪問距離為0*/
        this.dis[index] = 0;
    }

    /**
     * 判斷某各頂點是否被訪問過
     * @param index
     * @return
     */
    public boolean in(int index){
        return already_arr[index] == 1;
    }

    /**
     *  更新出發頂點到index節點的距離
     * @param index
     * @param len
     */
    public void updateDis(int index,int len){
        dis[index] = len;
    }

    /**
     * 更新pre這個頂點的前驅節點為index頂點
     * @param pre
     * @param index
     */
    public void updatePre(int pre,int index){
        pre_visited[pre] = index;
    }

    /**
     * 返回出發頂點到index的距離
     * @param index
     * @return
     */
    public int getDis(int index){
        return dis[index];
    }

    /**
     * 繼續選擇並返回新的訪問節點
     * @return
     */
    public int updateArr(){
        int min = 65535;
        int index = 0;
        for (int i = 0; i < already_arr.length;i++){
            if (already_arr[i] == 0 && dis[i] < min){ //使用廣度優先的策略,如果初始節點的下一個沒有被訪問,並且可以連通,就選擇下一個節點為出發節點
                min = dis[i];
                index = i;
            }
        }
        /*更新index頂點被訪問過*/
        already_arr[index] = 1;
        return index;
    }

    
}

  更新周圍頂點和前驅節點的距離,完成演算法

public void djs(int index){
        vv = new VisitedVertex(vertexes.length,index);
        /*更新index頂點到周圍頂點的距離和前驅節點*/
        update(index);
       
        for (int j = 1; j < vertexes.length; j++) {
            /*選擇並返回新的訪問節點*/
            index = vv.updateArr();
            /*更新index頂點到周圍頂點的距離和前驅節點*/
            update(index);
        }
    }

    /**
     * 更新index下標頂點周圍頂點的距離和周圍頂點的前驅節點
     * @param index
     */
    public void update(int index){
        int len = 0;
        /*遍歷鄰接矩陣的matrix[index]所對應的行*/
        for (int i = 0; i < matrix[index].length; i++) {
            /*出發頂點到index頂點的距離 + 從index到i頂點的距離和*/
            len = vv.getDis(index) + matrix[index][i];
            /*如果i頂點沒有被訪問過,並且len小於出發頂點到j頂點的距離,就需要更新*/
            if (!vv.in(i) && len < vv.getDis(i)){
                /*更新i點的前驅節點為index節點*/
                vv.updatePre(i,index);
                /*更新出發頂點到i的距離*/
                vv.updateDis(i,len);
            }
        }
    }

 執行結果:

          

 

   2.弗洛伊德演算法(求出所有頂點到其他各個頂點的最短距離)

    步驟:

      1.設頂點vi到vk的最短路徑已知是Lik,頂點vk到vj的最短路徑已知是Lkj,頂點vi到vj的路徑為Lij,那麼vi到vj的最短路徑是min((Lik+Lkj),Lij),vk的取值為圖中所有的頂點

       則可以獲得vi到vj的最短路徑

      2.vi到vk的最短路徑Lik,vj到vk的最短路徑Lkj,可以用同樣的方式獲得(遞迴)

  初始化圖

public static void main(String[] args) {
        char[] vertexes = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
        //建立鄰接矩陣
        int[][] matrix = new int[vertexes.length][vertexes.length];
        final int N = 65535;
        matrix[0] = new int[] { 0, 5, 7, N, N, N, 2 };
        matrix[1] = new int[] { 5, 0, N, 9, N, N, 3 };
        matrix[2] = new int[] { 7, N, 0, N, 8, N, N };
        matrix[3] = new int[] { N, 9, N, 0, N, 4, N };
        matrix[4] = new int[] { N, N, 8, N, 0, 5, 4 };
        matrix[5] = new int[] { N, N, N, 4, 5, 0, 6 };
        matrix[6] = new int[] { 2, 3, N, N, 4, 6, 0 };

        FGraph fGraph = new FGraph(vertexes,matrix,vertexes.length);
      
        fGraph.show();
    }

class FGraph{

    public char[] vertexes;  // 存放各個頂點的陣列
    public int[][] dis;     // 存放各個頂點到其他各頂點的距離
    public int[][] pre;    //  存放到達目標頂點的前驅節點

    /**
     * 初始化頂點,dis,pre
     * @param vertexes
     * @param matrix
     * @param length
     */
    public FGraph(char[] vertexes, int[][] matrix, int length) {
        this.vertexes = vertexes;
        this.dis = matrix;
        /*對pre陣列進行初始化,存放的是前驅節點的下標*/
        this.pre = new int[length][length];
        for (int i = 0; i < length; i++) {
            Arrays.fill(pre[i], i);
        }

    }


    public void show() {
        for(int k = 0; k < dis.length;k++){

            // 輸出pre
            for (int j = 0; j < dis.length; j++) {
                System.out.print(vertexes[pre[k][j]] + " ");
            }
            System.out.println();
            // 輸出dis
            for (int i = 0; i < dis.length; i++) {
                System.out.print("(" + vertexes[k] + "到"+vertexes[i]+"的最短路徑是: " + dis[k][i] + ") ");
            }
            System.out.println();
        }
    }
}

  實現演算法(vki+vkj < vij)

 public void floyd(){
        int len = 0; // 儲存距離
        /*對中間節點進行遍歷,k就是中間節點的下標 [A, B, C, D, E, F, G] */
        for (int k = 0; k < dis.length; k++) {
            /*從i頂點開始出發 [A, B, C, D, E, F, G] */
            for (int i = 0; i < dis.length; i++) {
                /*到達j頂點 [A, B, C, D, E, F, G] */
                for (int j = 0; j < dis.length; j++) {
                    /*計算出從i出發,經過k中間節點,到達j的距離*/
                    len = dis[i][k] + dis[k][j];
                    if (len < dis[i][j]) {/*如果len小於原本的距離*/
                        /*更新距離表*/
                        dis[i][j] = len;
                        /*更新前驅節點*/
                        pre[i][j] = pre[k][j];
                    }
                }
            }
        }
    }

執行結果: