1. 程式人生 > 其它 >普利姆演算法之最短路徑問題詳解

普利姆演算法之最短路徑問題詳解

普利姆演算法之最短路徑問題詳解

說明

  1. 普利姆演算法是一個求最短路徑的演算法,即給定一個帶權的無向圖,求一條路徑使得將這些節點連線後帶權路徑最短,即如何生成最小生成樹
  2. 以修路問題為例,假設有7個村莊,修一條通路連線這7個村莊,但是要求路徑最短
  3. 使用無向圖來模擬,圖的頂點為村莊,帶權路徑為村莊的通路,則轉化為求圖的最小權值問題
  4. 使用鄰接矩陣來表示圖
  5. 將鄰接矩陣建立好後,實現普利姆演算法,思路???
  6. 仍然建立一個大小等於節點數目的一維陣列表示當前節點是否訪問過,如果訪問過,則置1,如果沒有訪問過則置0
  7. 從某一節點開始,先將當前節點置為1,表示訪問過
  8. 因為有7個節點,所有至少需要6條通路才能連線所有的頂點,所以最外層迴圈總共迴圈6次,每次迴圈都尋找一條最短路徑
  9. 內層迴圈則要在已經訪問過的和沒有訪問過的節點連線中尋找最短路徑,找到後記錄兩個節點並記錄權值
  10. 通過6次大迴圈後則會找到6條路徑將所有的節點連線起來並能保證路徑最短
  11. 原始碼見下

原始碼及分析

package algorithm.algorithm.prim;

import java.util.Arrays;

/**
 * @author AIMX_INFO
 * @version 1.0
 */
public class PrimAlgorithm {
    public static void main(String[] args) {
        //頂點元素
        char[] data = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        //頂點個數
        int vertex = data.length;
        //設定最小生成樹的權值
        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}
        };
        //圖物件
        MGraph graph = new MGraph(vertex);
        //最小生成樹
        MinTree minTree = new MinTree();
        //鄰接矩陣
        minTree.createGraph(graph, vertex, data, weight);
        //minTree.showGraph(graph);
        //普利姆演算法
        minTree.prim(graph,3);

    }
}

//建立最小生成樹
class MinTree {
    //建立圖的鄰接矩陣
    /**
     * @param graph  圖
     * @param vertex 頂點個數
     * @param data   頂點的資料
     * @param weight 邊的權值
     */
    public void createGraph(MGraph graph, int vertex, char[] data, int[][] weight) {

        for (int i = 0; i < vertex; i++) {
            //給每個頂點賦值
            graph.data[i] = data[i];
            //給每條路徑賦權值
            for (int j = 0; j < vertex; j++) {
                graph.weight[i][j] = weight[i][j];
            }
        }
    }

    /**
     *
     * @param graph 最小生成樹
     * @param v 從哪個節點開始
     */
    public void prim(MGraph graph, int v){
        //建立一維陣列判斷是否訪問過,如果訪問過,則置1,否則置為0
        int[] isVisited = new int[graph.vertex];
        //從當前節點開始,則當前節點已經被訪問過
        //定義變數h1 h2儲存最小路徑的兩個頂點下標
        int h1 = -1, h2 = -1;
        //定義minWeight儲存最小路徑
        int minWeight = 10000;
        isVisited[v] = 1;
        //因為有graph.vertex個頂點,因此至少需要graph.vertex - 1條線連線所有的頂點
        for (int k = 1; k < graph.vertex; k++) {
            //尋找 已經訪問過的節點和其他未訪問過的鄰接節點之間的最小路徑
            for (int i = 0; i < graph.vertex; i++) {
                for (int j = 0; j < graph.vertex; j++) {
                    //i節點訪問過,j節點沒有訪問過並且兩節點之間的路徑最短,則記錄權值和兩個節點
                    if (isVisited[i] == 1 && isVisited[j] == 0 && graph.weight[i][j] < minWeight){
                        minWeight = graph.weight[i][j];
                        h1 = i;
                        h2 = j;
                    }
                }
            }
            //內兩層迴圈結束後則找到一條最短路徑
            System.out.println("邊 " + graph.data[h1] + " " + graph.data[h2] + " 權值為 " + graph.weight[h1][h2]);
            //將當前節點標記為已經訪問
            isVisited[h2] = 1;
            //重置權值
            minWeight = 10000;
        }
    }

    //檢視最小生成樹
    public void showGraph(MGraph graph) {
        for (int[] link : graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }
}

//圖類
class MGraph {
    //節點個數
    int vertex;
    //節點資料
    char[] data;
    //節點的邊,即鄰接矩陣
    int[][] weight;

    //構造器,通過節點個數建立圖
    public MGraph(int vertex) {
        this.vertex = vertex;
        data = new char[vertex];
        weight = new int[vertex][vertex];
    }
}