天天看點

普利姆算法(Prim)解決修路問題

普利姆算法(Prim)解決修路問題

普利姆算法(Prim)解決修路問題
普利姆算法(Prim)解決修路問題
普利姆算法(Prim)解決修路問題
普利姆算法(Prim)解決修路問題

普利姆算法應用目前問題得到的最小生成樹思路圖解:

普利姆算法(Prim)解決修路問題

java代碼

package com.bingym.prim;

import java.util.Arrays;

public class PrimAlgorithm {
    /*
    * 普利姆算法:
    * 典型的修路問題:
    * 1)有勝利鄉有7個村莊(A, B, C, D, E, F, G) ,現在需要修路把7個村莊連通
    * 2)各個村莊的距離用邊線表示(權) ,比如 A – B 距離 5公裡
    * 3)問:如何修路保證各個村莊都能連通,并且總的修建公路總裡程最短?
    * 思路: 将10條邊,連接配接即可,但是總的裡程數不是最小.
    * 正确的思路,就是盡可能的選擇少的路線,并且每條路線最小,保證總裡程數最少.
    * 修路問題本質就是就是最小生成樹問題:
    * 先介紹一下最小生成樹(Minimum Cost Spanning Tree),簡稱MST。
    * 1)給定一個帶權的無向連通圖,如何選取一棵生成樹,使樹上所有邊上權的總和為最小,這叫最小生成樹
    * 2)N個頂點,一定有N-1條邊
    * 3)包含全部頂點
    * 4)N-1條邊都在圖中
    * 普裡姆算法生成最小生成樹的思路:
    * 普利姆(Prim)算法求最小生成樹,也就是在包含n個頂點的連通圖中,找出隻有(n-1)條邊包含所有n個頂點的連通子圖,也就是所謂的極小連通子圖
    * 普利姆的算法如下:
    * 設G=(V,E)是連通網,T=(U,D)是最小生成樹,V,U是頂點集合,E,D是邊的集合 
    * 若從頂點u開始構造最小生成樹,則從集合V中取出頂點u放入集合U中,标記頂點v的visited[u]=1
    * 若集合U中頂點ui與集合V-U中的頂點vj之間存在邊,則尋找這些邊中權值最小的邊,但不能構成回路,将頂點vj加入集合U中,将邊(ui,vj)加入集合D中,标記visited[vj]=1
    * 重複步驟②,直到U與V相等,即所有頂點都被标記為通路過,此時D中有n-1條邊
    * */
    public static void main(String[] args) {
        //測試看看圖是否建立ok
        char[] data = new char[]{'A','B','C','D','E','F','G'};
        int verxNums = data.length;
        //鄰接矩陣的關系使用二維數組表示,10000這個大數,表示兩個點不聯通
        int [][]weight=new int[][]{
            {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 graph = new Graph(verxNums);
        //建立最小生成樹對象
        MinTree minTree = new MinTree();
        //建立Graph
        minTree.createGraph(graph,verxNums,data,weight);
        //輸出圖
        System.out.println("修路圖的鄰接矩陣為:");
        minTree.showGraph(graph);
        //測試普利姆算法
        System.out.println("使得路的總裡程數最少的路線為:");
        minTree.prim(graph,0);//0:表示誰為開始的頂點(此處表示從A開始進行最小生成樹的建立)

    }

}
//建立最小生成樹
class MinTree {
    //建立圖的鄰接矩陣
    public void createGraph(Graph graph,int vertexNums,char[] data,int[][] weight) {
        int i,j;
        for (i = 0; i < vertexNums; i++) {
            graph.data[i] = data[i];
            for (j = 0; j < vertexNums; j++) {
                graph.weight[i][j] = weight[i][j];
            }
        }
    }
    //顯示圖的鄰接矩陣
    public void showGraph(Graph graph) {
        for (int[] link : graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }
    //編寫prim算法,擷取最小生成樹
    public void prim(Graph graph,int start) {
        //V表示進行最小生成樹的開始的頂點
        //定義數組visited[]:标記結點(頂點)是否被通路過:預設初始全為0(表示都未通路過)
        int[] visited = new int[graph.vertexNums];
        //标記目前開始的結點的為已經通路
        visited[start] = 1;
        //定義兩個下标:表示兩個頂點的下标
        int vertex1 = -1;
        int vertex2 = -1;
        int minWeight = 10000;//先将minWeight初始化一個很大的數,對于路的權值近似為無窮大
        for (int k = 1; k < graph.vertexNums; k++) {
            //因為有 graph.verxs頂點,普利姆算法結束後,有 graph.verxs-1邊
            for (int i = 0; i < graph.vertexNums; i++) {//i表示已經通路過的結點
                for (int j = 0; j < graph.vertexNums; j++) {//j表示還未通路的結點
                  if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight) {
                      //替換minWeight(尋找已經通路過的結點和未通路過的結點間的權值最小的邊)
                      minWeight = graph.weight[i][j];
                      //同時記錄此時邊的兩個頂點
                      vertex1 = i;
                      vertex2 = j;
                  }

                }
            }
            //此時for循環以後找到了一條最小的了
            System.out.println("邊<" + graph.data[vertex1] + "," + graph.data[vertex2] + "> 權值:" + minWeight);
            //将此前未通路的j置為已通路的
            visited[vertex2] = 1;
            //重新将minWeight置為最大值
            minWeight = 10000;
        }
    }
}

//定義一個圖
class Graph {
    //圖的結點的個數(即頂點)的個數
    int vertexNums;
    int[][] weight;//存放邊,即鄰接矩陣
    char[] data;//存放結點的資料

    public Graph(int vertexNums) {
        this.vertexNums = vertexNums;
        //初始化結點數組
        data = new char[vertexNums];
        //初始化鄰接矩陣
        weight = new int[vertexNums][vertexNums];
    }
}