其實我在學最短路之前就學了生成樹了,現在接着寫。
本文介紹2種算法:Kruskal,Prim
PS:文中分大小寫。 圖爲G(V,E),V爲節點集合,E爲邊集合,但v表示某個節點(v∈V)
其實很多都和最短路差不多的,鬆弛操作不同而已。
前提:連通圖
Kruskal:
- 原理: 通過排序每一條邊(權值遞增)從|E|條邊中取V-1條邊出來(V個點嘛,最小生成樹始終是V-1條邊的),滿足每次選的邊的起點和終點不在一棵樹上。
- 採用: 並查集
- 步驟:
- 初始化邊,並以升序排序
- 初始化並查集,這裏以p域代表, p[i]=i; // i∈[1,|V|]
- 循環(1~|E| : i)
1)判斷第i條邊的起點和終點是否在一棵樹上(使用並查集,可保證非常快的判斷)
2)如果不在的話,加入i邊,並使得p[起點]=終點 或 p[終點]=起點
並查集實現:
inline int find(int x)
{
int t = x;
while(p[x] != x) x = p[x];
p[t] = x;
return x;
}
- 分析:時間複雜度O(E) 並查集的時間太小 省略。
- 適用於:任何連通圖 稀疏圖
- 優化:我不會
Prim:
- 原理:通過|V|-1次加入樹中與v的邊, min{key[v] | v∈G-已生成的最小樹} 到最小樹中,來實現最小生成樹
- 採用:優先隊列 priority_queue (#include <queue> 要定義一個比較類來實現最小堆)
- 步驟:
- 初始化key域 key[i] = INF; // i∈[1,|V|]
- key[S]=0; // S爲最小生成樹的根
- 初始化vis域(已生成樹),且vis[i]=0; // i∈[1,|V|]
- 將S壓入優先隊列q
- 循環(q不爲空)
1)使u=q的隊頭,並將隊頭出隊
2)把u與樹中的邊加入到樹中,即設置u已經在樹中vis[u]=1;
3)對以u爲起點的邊進行僞鬆弛(這些邊的終點一定是不在樹中的,即vis[a]!=1)。並把這些邊的終點壓入隊列。
我說的僞鬆弛的指的是:if(key[i] > edge[u][i]) key[i] = edge[u][i];//注意,如果沒有u->i這條路徑,edge[u][i]一定要是INF,不要是0,這點在初始化時做
(如果初始化爲0的話,可以這樣 if(edge[u][i] && key[i] > edge[u][i]) key[i] = edge[u][i];)
(之所以叫僞鬆弛,因爲這和鬆弛太像了。)
- 分析:時間複雜度O(KV) K爲優先隊列解壓最小的實現
- 優化:優化堆的實現(不嫌麻煩就手打FIB堆。。優先隊列是用二叉堆的。)
//好像漏洞百出,以後再來更新,歡迎大家幫忙找漏洞,歡迎吐槽