Prim

理論

Prim是尋找從某頂點出發,到全部頂點的最短路徑。

最短路徑(邊)的數量是頂點的數量 - 1,才能是到達各頂點之間的最少路徑(邊)。

3個頂點最少連接的邊是2條。(頂點數量3 - 1 = 最少連接的邊數2)
img

4個頂點最少連接的邊是3條。(頂點數量4 - 1 = 最少連接的邊數3)
img

從一個圖中,找出最少的邊數,但可以連接所有頂點。
img

樹,是沒有迴路的,上圖中的紅色的線連接所有頂點,可以把它看成一棵樹。

要在圖Graph中找一棵樹,這一棵樹中文是最小成本展開樹Minimum Cost Spanning Tree,縮寫為MST,可以想像為要在圖中找最短路徑可以連接到各個頂點的樹,最短路徑也就是最小的成本。

Prim著重在頂點,由某個頂點出發,檢查此頂點出發的邊,那個最小。

找出最短路徑的邊與頂點

找出最短路徑的邊,尋找次數為(頂點的數量 - 1)。

第1次

把頂點0加入到訪問清單,從頂點0出發,有2條路,那一條更短?
img

頂點0 到 頂點1的路更短,把「頂點1」加入訪問清單。
頂點0已經在訪問清單中了,所以不再加入。
img

第2次

訪問清單目前有頂點0與頂點1。

頂點0:尋找跟頂點0連著的邊誰最短,排除掉已訪問過的邊。
img

頂點1:尋找跟頂點1連著的邊誰最短,排除掉已訪問過的邊。
img

以上結果,誰的邊最短?
img
頂點1 到 頂點2的路更短,最短路徑相連的「頂點2」,加入訪問清單。
頂點1已經在訪問清單中了,所以不再加入。

第3次

訪問清單目前有頂點0與頂點1與頂點2。

頂點0:尋找跟頂點0連著的邊誰最短,排除掉已訪問過的邊。
img

頂點1:尋找跟頂點1連著的邊誰最短,排除掉已訪問過的邊。
img

頂點2:尋找跟頂點2連著的邊誰最短,排除掉已訪問過的邊。
img

以上結果,誰的邊最短?
img
頂點2 到 頂點3的路更短,最短路徑相連的「頂點3」,加入訪問清單。
頂點2已經在訪問清單中了,所以不再加入。

準備工作

Prim演算法,要先把相鄰矩陣中,不能連接的邊全設為最大值MAX,包含自己連自己也要設為MAX。

1
2
3
4
5
matrix = new int[vertexLen][vertexLen];
matrix[0] = new int[]{MAX, 1, 10, MAX};
matrix[1] = new int[]{1, MAX, 2, 8};
matrix[2] = new int[]{10, 2, MAX, 3};
matrix[3] = new int[]{MAX, 8, 3, MAX};

visted陣列,記錄每一輪最短距離相連的頂點。

頂點 0 1 2 3
visted F F F F

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class Prim {
  private int[][] matrix;
  private int vertexLen = 4;
  public static final int MAX = 10000;
  // 訪問清單
  private boolean[] visted;

  public Prim() {
    // 不能連接的邊全設為最大值MAX,包含自己連自己也要設為MAX
    matrix = new int[vertexLen][vertexLen];
    matrix[0] = new int[]{MAX, 1, 10, MAX};
    matrix[1] = new int[]{1, MAX, 2, 8};
    matrix[2] = new int[]{10, 2, MAX, 3};
    matrix[3] = new int[]{MAX, 8, 3, MAX};
    visted = new boolean[vertexLen];
  }

  public void findShortDist() {
    // 把頂點0加入訪問清單
    visted[0] = true;
    // 儲存最短路徑,也就最短的邊
    List<int[]> result = new ArrayList<>();
    // 尋找最短路徑次數為頂點數量 - 1
    for (int i = 0; i < vertexLen - 1; i++) {
      findShortDistVertex(result);
    }
    // 印出最短路徑,也就是最短的邊
    for (int[] arr : result) {
      System.out.println(Arrays.toString(arr));
    }
  }

  public void findShortDistVertex(List<int[]> result) {
    // 把最小權重(距離)設為最大值MAX
    int minWeight = MAX;
    // 最短距離連接的頂點,預設-1
    int x = -1;
    // 最短距離連接的頂點,預設-1
    int y = -1;
    // 遍歷訪問清單(若為最短距離頂點,會被加入清單中)
    for (int i = 0; i < visted.length; i++) {
      // 頂點是否在清單中
      if (visted[i]) {
      	// 相連的邊那一條最短,而且相連的頂點未被訪問過
        for (int j = 0; j < vertexLen; j++) {
          // matrix[i][j]不為 MAX,代表頂點i與頂點j是有相連
          if (matrix[i][j] != MAX && !visted[j]) {
          	// 距離是否比最小距離小
            if (matrix[i][j] < minWeight) {
              // 替換成最小距離
              minWeight = matrix[i][j];
              // 記錄最小距離相連的頂點
              x = i;
              y = j;
            }
          }
        }
      }
    }
    System.out.println("x = " + x + ", y = " + y + ", weight = " + minWeight);
    // 把最小距離的頂點y加入清單
    // x頂點已在清單中,不用再加入
    visted[y] = true;
    // 儲存最短距離的邊
    result.add(new int[]{x, y});
  }

  public static void main(String[] args) {
    Prim prim = new Prim();
    prim.findShortDist();
  }
}
x = 0, y = 1, weight = 1
x = 1, y = 2, weight = 2
x = 2, y = 3, weight = 3
[0, 1]
[1, 2]
[2, 3]

results matching ""

    No results matching ""