普利姆演算法解決最短修路問題

cai_ing發表於2020-09-28

普利姆演算法

1、應用場景-修路問題

在這裡插入圖片描述

2、最小生成樹

在這裡插入圖片描述
在這裡插入圖片描述

3、普利姆演算法介紹

在這裡插入圖片描述
在這裡插入圖片描述

4、普利姆演算法的最為簡單的理解(重點):

  • 理論上7個點要6條路就可以連通,隨便從一個結點(村莊)出發(假設為A),先找該結點和鄰居結點(村莊)距離最小的結點(村莊),假設為B,最小路徑為A–>B ,這樣子A和B連通了。
    把B加入已訪問的集合中,則為(A,B)

  • 找A的其他鄰居和B的其他鄰居,然後找到距離A或者B最小的那個鄰居結點,假設為C點,且路徑為B–>C最小,則A–>B B–>C 這樣A通過B可以走到C,即ABC連通了。把C加入已訪問的集合中,則為(A,B,C)

  • 依次往後找,上一段為(A–>B B–>C),比較A,B,C的其他鄰居和A,B,C之間的距離,然後找到距離A或者B或者C最小的那個鄰居結點(村莊),假設為D點,且路徑為B–>D最小,則A–>B B–>C B–>D 這樣A通過B可以走到C,也可以走到D,即ABCD連通了。把D加入已訪問的集合中,則為(A,B,C,D)

  • 依次迴圈,n節點需走n-1個次(eg:【點–點--點–點(4個結點(村莊)3條最小路徑便能連通)】),就能構建出一條理論上最短的連通路徑(有點貪心演算法的味道了,每次都取相鄰距離最小的點,並且把該點入集合,然後下次再把集合中那幾個點的相鄰的最小的點放入集合,把所有n個點放完也就n-1次,就構建了n-1條短路,n個村莊就能連起來互通了)


5、普利姆演算法最佳實踐(修路問題)

在這裡插入圖片描述

程式碼實現:

package com.czl.prim;

import java.lang.reflect.Array;
import java.util.Arrays;

public class PrimAlgorithm {
    public static void main(String[] args) {
        MinTree minTree = new MinTree();
        //測試看看圖是否建立成功
        char[] data = new char[] {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        //鄰接矩陣的關係使用二維陣列表示,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}};
        //得到MGraph物件
        MGraph graph = minTree.createGraph(data, weight);
        for (int[] link : graph.weight) {
            System.out.println(Arrays.toString(link));
        }

        //測試普利姆演算法
        minTree.prim(graph, 1);


    }
}

//建立最小生成樹->村莊的圖
class MinTree {
    /**
     *
     * @param data 圖的各個頂點的值
     * @param weight 圖的鄰接矩陣
     * @return
     */
    public MGraph createGraph(char[] data, int[][] weight) {
        MGraph graph = new MGraph(data.length);
        graph.data = data;
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data.length; j++) {
                graph.weight[i][j] = weight[i][j];
            }
        }
        return graph;
    }

    //普利姆演算法得到最小生成樹

    /**
     *
     *  @param mGraph 圖
     * @param v 表示從圖的第幾個頂點開始生成 'A'->0  'B'->1 ...
     */
    public void prim(MGraph mGraph, int v) {
        //visited[] 標記結點(頂點)是否被訪問過。1為被訪問,0為沒訪問
        int[] visited = new int[mGraph.verxs];
        int minWeight = 10000;//初始化一個大數,後面在遍歷過程中,會被替換

        //把當前這個結點標記為已訪問
        visited[v] = 1;
        //h1和h2記錄兩個頂點的下標
        int h1 = -1;
        int h2 = -1;
        //有graph.verxs個頂點,普利姆演算法結束後,有graph.verxs-1條邊,因此這裡做graph,verxs-1次迴圈
       /*
        第一次迴圈,假設當前頂點為A,則尋找A相鄰頂點距離最小的頂點(村莊),假設為B ,且路徑為A --> B
             此時子圖為A,B  A-->B
        第二次迴圈,比較A到其他相鄰頂點和B到其他相鄰頂點(村莊)的距離,找到最小距離的那個頂點(村莊),  假設為C  且路徑為B-->C最小 ,
             此時子圖為 A,B ,C  A-->B B-->C(即A可以通過B到達C)
        第三次迴圈,比較A,B,C其他相鄰頂點的距離,找到最小距離的那個頂點,假設為D,且路徑為 B-->D
            此時子圖為A,B,C,D  A-->B B-->C B-->D(即A可以通過B到達D)
        第四次迴圈,比較A,B,C,D其他相鄰頂點的距離,找到最小距離的那個頂點,假設為E,路徑為 D-->E
            此時子圖為A,B,C,D,E   A-->B B -->C B-->D D -->E (即A通過B到達D,再通過D到達E)
        依次迴圈...
        直到n個頂點遍歷n-1次後找到n個頂點(村莊)連線的最小路徑
        */
        for (int k = 1; k < mGraph.verxs; k++) {

            //確定每一次生成的子圖,與那個結點最近
            //一次雙層for迴圈,可以找到子圖中已訪問的所有的點和其他相鄰頂點距離最小的頂點
            for (int i = 0; i < mGraph.verxs; i++) {//i結點表示被訪問過的節點
                for (int j = 0; j < mGraph.verxs; j++) {//j結點表示還沒訪問過的結點
                    //被訪問過的結點(visited[i] == 1 )和未訪問過的結點( visited[j] == 0 )之間的距離(權)<設定的minWeight(10000)
                    if (visited[i] == 1 && visited[j] == 0 && mGraph.weight[i][j] < minWeight) {

                            //替換minWeight(x尋找已經訪問過的結點和未訪問過的結點間的權值最小的邊)
                            minWeight = mGraph.weight[i][j];
                        h1 = i;
                        h2 = j;
                    }

                }

            }
            //找到一條邊最小
            System.out.println("邊<"+mGraph.data[h1]+","+mGraph.data[h2]+">權為:"+minWeight);
            //將當前這個結點標記為已經訪問
            visited[h2]=1;
            //重新設定為最大值10000
            minWeight = 10000;

        }


    }


}


//鄰接矩陣
class MGraph {
    int verxs;//表示圖的結點個數
    int weight[][];//存放邊,就是我們的鄰接矩陣
    char[] data;//存放結點資料

    public MGraph(int verx) {
        verxs = verx;
        weight = new int[verxs][verxs];
        data = new char[verxs];
    }

}

相關文章