1. 最小生成樹定義
樹是指沒有環路的圖,生成樹就是指一個圖上面刪除一些邊,使它沒有環路。
最小生成樹就是指生成樹中邊權之和最小的那一種。
上圖的最小生成樹就是這樣:
2. Prim 算法
2.1. 算法流程
就以上圖為例:
- 先選擇一個起始點,我們就以A為例。
- 創建一個集合S,用來存儲已經在樹中間的點。開始時集合那只有點A,即 \(S = \{A\}\)。
- 選擇一個連通到集合S中一個點的最小邊,其中它的另一個端點不在集合S中。以保證,最小生成樹不會形成環。把這條邊的不在S集合中的端點加到S集合中。(目前選邊AB, \(S = \{ A, B\}\))
- 重復步驟三,直到所有的點都在S集合中了。
- 答案就是剛才所選的邊的邊權和啦。
時間復雜度: \(O(nm+m)\)
2.2. 優化
這個算法的時間的主要瓶頸就是在我們尋找那一條邊的邊權最小的時候,那么注意到這里其實是可以通過堆優化的。代碼如下:
int ans = 0;
int index = 1;
h.push(point(0, 1));
while (index <= n) {
int x = h.top().id, d = h.top().w;
h.pop();
if (S[x]) continue;
S[x] = 1;
++index;
ans += d;
for (int i = 0; i < G[x].size(); ++i) {
int y = G[x][i].v, z = G[x][i].w;
if (!S[y]) {
h.push(point(z, y));
}
}
}
時間復雜度: \(O(n\log m + m)\)
3. kruskal 算法
3.1. 算法流程
還是以上圖為例:
- 首先第一步最開始,先給邊排序。
- 選擇一個邊權最小的邊,判斷它的兩個端點是否原來已經連通,如果沒有連通的話,就選這條邊。以保證這個樹上不會出現回路。
- 重復步驟二,直到選出\(n-1\)條邊為止.
- 上面流程得到的樹就是最小生成樹。
時間復雜度:\(O(n^2)\)
3.2. 優化
算法的主要時間瓶頸就是在如何判斷原來兩個點已經連通,如果用DFS或者BFS的話,效率較低,所以我們這里使用並查集優化。
sort(E.begin(), E.end(), cmp);
int index = 1, np = 0;
int ans = 0;
while (index <= n - 1) {
if (np >= E.size()) break;
node now = E[np++];
if (getf(now.u) == getf(now.v)) continue;
++index;
ans += now.w;
merage(now.u, now.v);
}
時間復雜度:\(O(m \log m+m \alpha (n))\)
by szdytom
